/* -*- 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):
 *  Mark Mentovai <mark@moxienet.com>
 *
 * 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 "nsMacEventHandler.h"

#include "nsWindow.h"
#include "nsMacWindow.h"
#include "nsCRT.h"
#include "prinrval.h"

#include <ToolUtils.h>
#include <TextServices.h>
#include <UnicodeConverter.h>

#include "nsCarbonHelpers.h"
#include "nsIRollupListener.h"
#include "nsIMenuRollup.h"
#include "nsGfxUtils.h"

#include "nsIDocument.h"
#include "nsIFrame.h"
#include "nsIObjectFrame.h"
#include "nsIPresShell.h"
#include "nsIEventStateManager.h"
#include "nsToolkit.h"

static nsMacEventHandler* sLastActive;

//#define DEBUG_TSM
extern nsIRollupListener * gRollupListener;
extern nsIWidget         * gRollupWidget;


// from MacHeaders.c
#ifndef topLeft
	#define topLeft(r)	(((Point *) &(r))[0])
#endif
#ifndef botRight
	#define botRight(r)	(((Point *) &(r))[1])
#endif

static void ConvertKeyEventToContextMenuEvent(const nsKeyEvent* inKeyEvent, nsMouseEvent* outCMEvent);
static inline PRBool IsContextMenuKey(const nsKeyEvent& inKeyEvent);


//-------------------------------------------------------------------------
//
//-------------------------------------------------------------------------
nsMacEventDispatchHandler::nsMacEventDispatchHandler()
{
	mActiveWidget	= nsnull;
	mWidgetHit		= nsnull;
	mWidgetPointed	= nsnull;
#if TRACK_MOUSE_LOC
  mLastGlobalMouseLoc.h = mLastGlobalMouseLoc.v = 0;
#endif
}


//-------------------------------------------------------------------------
//
//-------------------------------------------------------------------------
nsMacEventDispatchHandler::~nsMacEventDispatchHandler()
{
	if (mActiveWidget)
	{
	  mActiveWidget->RemoveDeleteObserver(this);
	  mActiveWidget = nsnull;
	}

	if (mWidgetHit)
	{
	  mWidgetHit->RemoveDeleteObserver(this);
	  mWidgetHit = nsnull;
	}

	if (mWidgetPointed)
	{
	  mWidgetPointed->RemoveDeleteObserver(this);
	  mWidgetPointed = nsnull;
	}	
}


//-------------------------------------------------------------------------
//
//-------------------------------------------------------------------------
void nsMacEventDispatchHandler::DispatchGuiEvent(nsWindow *aWidget, PRUint32 aEventType)
{
	NS_ASSERTION(aWidget,"attempted to dispatch gui event to null widget");
	if (!aWidget)
		return;

	nsGUIEvent guiEvent(PR_TRUE, aEventType, aWidget);
	guiEvent.time = PR_IntervalNow();
	aWidget->DispatchWindowEvent(guiEvent);
}

//-------------------------------------------------------------------------
//
//-------------------------------------------------------------------------
void nsMacEventDispatchHandler::DispatchSizeModeEvent(nsWindow *aWidget, nsSizeMode aMode)
{
	NS_ASSERTION(aWidget,"attempted to dispatch gui event to null widget");
	if (!aWidget)
		return;

	nsSizeModeEvent event(PR_TRUE, NS_SIZEMODE, aWidget);
	event.time = PR_IntervalNow();
	event.mSizeMode = aMode;
	aWidget->DispatchWindowEvent(event);
}

//-------------------------------------------------------------------------
//
//-------------------------------------------------------------------------
void nsMacEventDispatchHandler::SetFocus(nsWindow *aFocusedWidget)
{
    // This short circut lives inside of nsEventStateManager now
	if (aFocusedWidget == mActiveWidget)
		return;
		
	// tell the old widget it is not focused
	if (mActiveWidget)
	{
		mActiveWidget->ResetInputState();
		mActiveWidget->RemoveDeleteObserver(this);
		//printf("nsMacEventDispatcher::SetFocus sends NS_LOSTFOCUS\n");
		DispatchGuiEvent(mActiveWidget, NS_LOSTFOCUS);
	}

	mActiveWidget = aFocusedWidget;

	// let the new one know it got the focus
	if (mActiveWidget)
	{
		mActiveWidget->AddDeleteObserver(this);
		//printf("nsMacEventDispatcher::SetFocus sends NS_GOTFOCUS\n");
		DispatchGuiEvent(mActiveWidget, NS_GOTFOCUS);
	}
}

//-------------------------------------------------------------------------
//
//-------------------------------------------------------------------------

void nsMacEventDispatchHandler::SetActivated(nsWindow *aActivatedWidget)
{
  //printf("nsMacEventDispatcher::SetActivated \n");
  
	if (aActivatedWidget == mActiveWidget) {
		//printf("already the active widget. Bailing!\n");
		return;
	}

  if(aActivatedWidget) {
    nsWindowType wtype;
    aActivatedWidget->GetWindowType(wtype);
    if ( eWindowType_popup == wtype ) {
      //printf("nsMacEventDispatcher::SetActivated type popup, bail\n");
      return;
    }
  }
  
	// tell the old widget it is not focused
	if (mActiveWidget)
	{
		mActiveWidget->ResetInputState();
		mActiveWidget->RemoveDeleteObserver(this);
		//printf("nsMacEventDispatchHandler::SetActivated sends NS_LOSTFOCUS\n");
		DispatchGuiEvent(mActiveWidget, NS_LOSTFOCUS);
	}

	mActiveWidget = aActivatedWidget;

	// let the new one know it got activation
	if (mActiveWidget)
	{
		mActiveWidget->AddDeleteObserver(this);
		//printf("nsMacEventDispatcher::SetActivated sends NS_GOTFOCUS\n");
		DispatchGuiEvent(mActiveWidget, NS_GOTFOCUS);
		
		//printf("nsMacEventDispatchHandler::SetActivated sends NS_ACTIVATE\n");
		DispatchGuiEvent(mActiveWidget, NS_ACTIVATE);
	}
}

//-------------------------------------------------------------------------
//
//-------------------------------------------------------------------------

void nsMacEventDispatchHandler::SetDeactivated(nsWindow *aDeactivatedWidget)
{
    //printf("nsMacEventDispatchHandler::SetDeactivated\n");
    if(aDeactivatedWidget) {
      nsWindowType wtype;
      aDeactivatedWidget->GetWindowType(wtype);
      if ( eWindowType_popup == wtype ) {
        //printf("nsMacEventDispatchHandler::SetDeactivated type popup, bail\n");
        return;
      }
    }
      
    // If the deactivated toplevel window contains mActiveWidget, then
    // clear out mActiveWidget.

    if (mActiveWidget) {
      nsCOMPtr<nsIWidget> curWin = do_QueryInterface(NS_STATIC_CAST(nsIWidget*, mActiveWidget));
      for (;;) {
        nsCOMPtr<nsIWidget> parent = dont_AddRef(curWin->GetParent());
        if (!parent)
          break;
        curWin = parent;
      }

      if (NS_STATIC_CAST(nsWindow*, NS_STATIC_CAST(nsIWidget*, curWin)) == aDeactivatedWidget) {
        //printf("   nsMacEventDispatchHandler::SetDeactivated sends NS_DEACTIVATE\n");
        mActiveWidget->RemoveDeleteObserver(this);
        mActiveWidget = nsnull;
      }
    }

    DispatchGuiEvent(aDeactivatedWidget, NS_DEACTIVATE);	
}

//-------------------------------------------------------------------------
//
//-------------------------------------------------------------------------
void nsMacEventDispatchHandler::SetWidgetHit(nsWindow *aWidgetHit)
{
	if (aWidgetHit == mWidgetHit)
		return;

	if (mWidgetHit)
	  if (! mWidgetHit->RemoveDeleteObserver(this))
	  	NS_WARNING("nsMacFocusHandler wasn't in the WidgetHit observer list");

	mWidgetHit = aWidgetHit;

	if (mWidgetHit)
	  mWidgetHit->AddDeleteObserver(this);
}


//-------------------------------------------------------------------------
//
//-------------------------------------------------------------------------
void nsMacEventDispatchHandler::SetWidgetPointed(nsWindow *aWidgetPointed)
{
	if (aWidgetPointed == mWidgetPointed) {
		return;
    }
       
	if (mWidgetPointed) 
	  if (! mWidgetPointed->RemoveDeleteObserver(this)) 
	  	NS_WARNING("nsMacFocusHandler wasn't in the WidgetPointed observer list");
    
	mWidgetPointed = aWidgetPointed;
    
	if (mWidgetPointed)
	  mWidgetPointed->AddDeleteObserver(this);
}


//-------------------------------------------------------------------------
//
//-------------------------------------------------------------------------
void nsMacEventDispatchHandler::NotifyDelete(void* aDeletedObject)
{
	if (mActiveWidget == aDeletedObject)
		mActiveWidget = nsnull;
	
	if (mWidgetHit == aDeletedObject)
		mWidgetHit = nsnull;

	if (mWidgetPointed == aDeletedObject)
		mWidgetPointed = nsnull;

}


#if TRACK_MOUSE_LOC

void nsMacEventDispatchHandler::SetGlobalPoint(Point inPoint)
{
  mLastGlobalMouseLoc = inPoint;
}

#endif


#pragma mark -

//-------------------------------------------------------------------------
//
// nsMacEventHandler constructor/destructor
//
//-------------------------------------------------------------------------
nsMacEventHandler::nsMacEventHandler(nsMacWindow* aTopLevelWidget,
                                     nsMacEventDispatchHandler* aEventDispatchHandler)
{
  OSErr err;
  InterfaceTypeList supportedServices;

  mTopLevelWidget = aTopLevelWidget;

  //
  // create a TSMDocument for this window.  We are allocating a TSM document for
  // each Mac window
  //
  mTSMDocument = nsnull;
  supportedServices[0] = kUnicodeDocument;
  err = ::NewTSMDocument(1, supportedServices,&mTSMDocument, (long)this);
  NS_ASSERTION(err==noErr, "nsMacEventHandler::nsMacEventHandler: NewTSMDocument failed.");
#ifdef DEBUG_TSM
  printf("nsMacEventHandler::nsMacEventHandler: created TSMDocument[%p]\n", mTSMDocument);
#endif

  // make sure we do not use input widnow even some other code turn it for default by calling 
  // ::UseInputWindow(nsnull, TRUE); 
  if (mTSMDocument)
    ::UseInputWindow(mTSMDocument, FALSE); 
         
  mIMEIsComposing = PR_FALSE;
  mIMECompositionStr = nsnull;

  mKeyIgnore = PR_FALSE;
  mKeyHandled = PR_FALSE;

  mLastModifierState = 0;

  mMouseInWidgetHit = PR_FALSE;

  ClearLastMouseUp();

  if (aEventDispatchHandler) {
    mEventDispatchHandler = aEventDispatchHandler;
    mOwnEventDispatchHandler = PR_FALSE;
  }
  else {
    mEventDispatchHandler = new nsMacEventDispatchHandler();
    mOwnEventDispatchHandler = PR_TRUE;
  }
}


nsMacEventHandler::~nsMacEventHandler()
{
	if (mTSMDocument)
		(void)::DeleteTSMDocument(mTSMDocument);
	if(nsnull != mIMECompositionStr) {
		delete mIMECompositionStr;
		mIMECompositionStr = nsnull;
	}

	if (mOwnEventDispatchHandler)
		delete mEventDispatchHandler;

  if (sLastActive == this) {
    // This shouldn't happen
    sLastActive = nsnull;
  }
}



#pragma mark -
//-------------------------------------------------------------------------
//
// HandleOSEvent
//
//-------------------------------------------------------------------------
PRBool nsMacEventHandler::HandleOSEvent ( EventRecord& aOSEvent )
{
	PRBool retVal = PR_FALSE;

	switch (aOSEvent.what)
	{
		case mouseDown:
			retVal = HandleMouseDownEvent(aOSEvent);
			break;

		case mouseUp:
			retVal = HandleMouseUpEvent(aOSEvent);
			break;

		case osEvt:
		{
			unsigned char eventType = ((aOSEvent.message >> 24) & 0x00ff);
			if (eventType == mouseMovedMessage)
			{
				retVal = HandleMouseMoveEvent(aOSEvent);
			}
		}
		break;
	}

	return retVal;
}


#if USE_MENUSELECT

//-------------------------------------------------------------------------
//
// Handle Menu commands
//
//-------------------------------------------------------------------------
PRBool nsMacEventHandler::HandleMenuCommand(
  EventRecord& aOSEvent,
  long         aMenuResult)
{
	// get the focused widget
	nsWindow* focusedWidget = mEventDispatchHandler->GetActive();
	if (!focusedWidget)
		focusedWidget = mTopLevelWidget;

	// nsEvent
	nsMenuEvent menuEvent(PR_TRUE, NS_MENU_SELECTED, focusedWidget);
	menuEvent.point.x		= aOSEvent.where.h;
	menuEvent.point.y		= aOSEvent.where.v;
	menuEvent.time			= PR_IntervalNow();

	// nsGUIEvent
	menuEvent.nativeMsg	= (void*)&aOSEvent;

	// nsMenuEvent
  // TODO: initialize mMenuItem
	menuEvent.mCommand	= aMenuResult;

	// dispatch the menu event
	PRBool eventHandled = focusedWidget->DispatchWindowEvent(menuEvent);
	
	// if the menu event is not processed by the focused widget, propagate it
	// through the different parents all the way up to the top-level window
	if (! eventHandled)
	{
		// make sure the focusedWidget wasn't changed or deleted
		// when we dispatched the event (even though if we get here, 
		// the event is supposed to not have been handled)
		if (focusedWidget == mEventDispatchHandler->GetActive())
		{
			nsCOMPtr<nsIWidget> grandParent;
			nsCOMPtr<nsIWidget> parent ( dont_AddRef(focusedWidget->GetParent()) );
			while (parent)
			{
				menuEvent.widget = parent;
				eventHandled = (static_cast<nsWindow*>(static_cast<nsIWidget*>(parent)))->DispatchWindowEvent(menuEvent);
				if (eventHandled)
				{
					break;
				}
				else
				{
					grandParent = dont_AddRef(parent->GetParent());
					parent = grandParent;
				}
			}
		}
	}

	return eventHandled;
}

#endif


void
nsMacEventHandler::InitializeMouseEvent(nsMouseEvent& aMouseEvent,
                                        nsPoint&      aPoint,
                                        PRInt16       aModifiers,
                                        PRUint32      aClickCount)
{
  // nsEvent
  aMouseEvent.point      = aPoint;
  aMouseEvent.time       = PR_IntervalNow();

  // nsInputEvent
  aMouseEvent.isShift    = ((aModifiers & shiftKey)   != 0);
  aMouseEvent.isControl  = ((aModifiers & controlKey) != 0);
  aMouseEvent.isAlt      = ((aModifiers & optionKey)  != 0);
  aMouseEvent.isMeta     = ((aModifiers & cmdKey)     != 0);

  // nsMouseEvent
  aMouseEvent.clickCount = aClickCount;
}


//-------------------------------------------------------------------------
//
// DragEvent
//
//-------------------------------------------------------------------------
//
// Someone on the outside told us that something related to a drag is
// happening.  The exact event type is passed in as |aMessage|. We need to
// send this event into Gecko for processing.  Create a Gecko event (using
// the appropriate message type) and pass it along.
//
PRBool nsMacEventHandler::DragEvent(unsigned int aMessage,
                                    Point        aMouseGlobal,
                                    UInt16       aKeyModifiers)
{
  // Convert the mouse to local coordinates.  We have to do all the funny port
  // origin stuff just in case it has been changed.
  Point hitPointLocal = aMouseGlobal;
  WindowRef wind =
   NS_STATIC_CAST(WindowRef, mTopLevelWidget->GetNativeData(NS_NATIVE_DISPLAY));
  nsGraphicsUtils::SafeSetPortWindowPort(wind);
  {
    StOriginSetter  originSetter(wind);
    ::GlobalToLocal(&hitPointLocal);
  }
  nsPoint widgetHitPoint(hitPointLocal.h, hitPointLocal.v);

  nsWindow* widgetHit = mTopLevelWidget->FindWidgetHit(hitPointLocal);
  if (widgetHit) {
    // Adjust from local coordinates to window coordinates in case the hit
    // widget isn't at (0, 0).
    nsRect bounds;
    widgetHit->GetBounds(bounds);
    nsPoint widgetOrigin(bounds.x, bounds.y);
    widgetHit->LocalToWindowCoordinate(widgetOrigin);
    widgetHitPoint.MoveBy(-widgetOrigin.x, -widgetOrigin.y);		
  }

  // Note that aMessage will be NS_DRAGDROP_(ENTER|EXIT|OVER|DROP) depending
  // on the state of the drag relative to the top-level window, not the
  // widget that interests us.  As a result, NS_DRAGDROP_ENTER and
  // NS_DRAGDROP_EXIT events must be synthesized as the drag moves between
  // widgets in this window.

  if (aMessage == NS_DRAGDROP_EXIT) {
    // If the drag is leaving the window, it can't be over a widget contained
    // within the window.
    widgetHit = nsnull;
  }

  nsWindow* lastWidget = mEventDispatchHandler->GetWidgetPointed();
  if (lastWidget != widgetHit) {
    if (aMessage != NS_DRAGDROP_ENTER) {
      if (lastWidget) {
        // Send an NS_DRAGDROP_EXIT event to the last widget.  This is not done
        // if aMessage == NS_DRAGDROP_ENTER, because that indicates that the
        // drag is either beginning (in which case there is no valid last
        // widget for the drag) or reentering the window (in which case
        // NS_DRAGDROP_EXIT was sent when the mouse left the window).

        nsMouseEvent exitEvent(PR_TRUE, NS_DRAGDROP_EXIT, lastWidget,
                               nsMouseEvent::eReal);
        nsPoint zero(0, 0);
        InitializeMouseEvent(exitEvent, zero, aKeyModifiers, 1);
        lastWidget->DispatchMouseEvent(exitEvent);
      }

      if (aMessage != NS_DRAGDROP_EXIT && widgetHit) {
        // Send an NS_DRAGDROP_ENTER event to the new widget.  This is not
        // done when aMessage == NS_DRAGDROP_EXIT, because that indicates
        // that the drag is leaving the window.

        nsMouseEvent enterEvent(PR_TRUE, NS_DRAGDROP_ENTER, widgetHit,
                                nsMouseEvent::eReal);
        InitializeMouseEvent(enterEvent, widgetHitPoint, aKeyModifiers, 1);
        widgetHit->DispatchMouseEvent(enterEvent);
      }
    }
  }

  // update the tracking of which widget the mouse is now over.
  mEventDispatchHandler->SetWidgetPointed(widgetHit);

  if (!widgetHit) {
    // There is no widget to receive the event.  This happens when the drag
    // leaves the window or when the drag moves over a part of the window
    // not containing any widget, such as the title bar.  When appropriate,
    // NS_DRAGDROP_EXIT was dispatched to lastWidget above, so there's nothing
    // to do but return.
    return PR_TRUE;
  }

  nsMouseEvent geckoEvent(PR_TRUE, aMessage, widgetHit, nsMouseEvent::eReal);
  InitializeMouseEvent(geckoEvent, widgetHitPoint, aKeyModifiers, 1);
  widgetHit->DispatchMouseEvent(geckoEvent);

  return PR_TRUE;
}


#pragma mark -

//-------------------------------------------------------------------------
//
// ConvertMacToRaptorKeyCode
//
//-------------------------------------------------------------------------


// Key code constants
enum
{
	kEscapeKeyCode			= 0x35,
	kCommandKeyCode     = 0x37,
	kShiftKeyCode				= 0x38,
	kCapsLockKeyCode		= 0x39,
	kControlKeyCode			= 0x3B,
	kOptionkeyCode			= 0x3A,		// left and right option keys
	kClearKeyCode				= 0x47,
	
	// 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,
	
	kKeypadMultiplyKeyCode	= 0x43,
	kKeypadAddKeyCode				= 0x45,
	kKeypadSubtractKeyCode	= 0x4E,
	kKeypadDecimalKeyCode		= 0x41,
	kKeypadDivideKeyCode		= 0x4B,
	kKeypadEqualsKeyCode		= 0x51,			// no correpsonding raptor key code
	kEnterKeyCode           = 0x4C,
	kReturnKeyCode          = 0x24,
	kPowerbookEnterKeyCode  = 0x34,     // Enter on Powerbook's keyboard is different
	
	kHelpKeyCode						= 0x72,				// also insert key
	kDeleteKeyCode					= 0x75,				// also forward delete key
	kTabKeyCode							= 0x30,
	kBackspaceKeyCode       = 0x33,
	kHomeKeyCode						= 0x73,	
	kEndKeyCode							= 0x77,
	kPageUpKeyCode					= 0x74,
	kPageDownKeyCode				= 0x79,
	kLeftArrowKeyCode				= 0x7B,
	kRightArrowKeyCode			= 0x7C,
	kUpArrowKeyCode					= 0x7E,
	kDownArrowKeyCode				= 0x7D
	
};

static PRUint32 ConvertMacToRaptorKeyCode(char charCode, UInt32 keyCode, UInt32 eventModifiers)
{
	PRUint32	raptorKeyCode = 0;
	
	switch (keyCode)
	{
//  case ??            :       raptorKeyCode = nsIDOMKeyEvent::DOM_VK_CANCEL;    break;     // don't know what this key means. Nor does joki

// modifiers. We don't get separate events for these
    case kShiftKeyCode:         raptorKeyCode = nsIDOMKeyEvent::DOM_VK_SHIFT;         break;
    case kCommandKeyCode:       raptorKeyCode = nsIDOMKeyEvent::DOM_VK_META;          break;
    case kCapsLockKeyCode:      raptorKeyCode = nsIDOMKeyEvent::DOM_VK_CAPS_LOCK;     break;
    case kControlKeyCode:       raptorKeyCode = nsIDOMKeyEvent::DOM_VK_CONTROL;       break;
    case kOptionkeyCode:        raptorKeyCode = nsIDOMKeyEvent::DOM_VK_ALT;           break;

// function keys
    case kF1KeyCode:            raptorKeyCode = nsIDOMKeyEvent::DOM_VK_F1;            break;
    case kF2KeyCode:            raptorKeyCode = nsIDOMKeyEvent::DOM_VK_F2;            break;
    case kF3KeyCode:            raptorKeyCode = nsIDOMKeyEvent::DOM_VK_F3;            break;
    case kF4KeyCode:            raptorKeyCode = nsIDOMKeyEvent::DOM_VK_F4;            break;
    case kF5KeyCode:            raptorKeyCode = nsIDOMKeyEvent::DOM_VK_F5;            break;
    case kF6KeyCode:            raptorKeyCode = nsIDOMKeyEvent::DOM_VK_F6;            break;
    case kF7KeyCode:            raptorKeyCode = nsIDOMKeyEvent::DOM_VK_F7;            break;
    case kF8KeyCode:            raptorKeyCode = nsIDOMKeyEvent::DOM_VK_F8;            break;
    case kF9KeyCode:            raptorKeyCode = nsIDOMKeyEvent::DOM_VK_F9;            break;
    case kF10KeyCode:           raptorKeyCode = nsIDOMKeyEvent::DOM_VK_F10;           break;
    case kF11KeyCode:           raptorKeyCode = nsIDOMKeyEvent::DOM_VK_F11;           break;
    case kF12KeyCode:           raptorKeyCode = nsIDOMKeyEvent::DOM_VK_F12;           break;
//  case kF13KeyCode:           raptorKeyCode = nsIDOMKeyEvent::DOM_VK_F13;           break;    // clash with the 3 below
//  case kF14KeyCode:           raptorKeyCode = nsIDOMKeyEvent::DOM_VK_F14;           break;
//  case kF15KeyCode:           raptorKeyCode = nsIDOMKeyEvent::DOM_VK_F15;           break;
    case kPauseKeyCode:         raptorKeyCode = nsIDOMKeyEvent::DOM_VK_PAUSE;         break;
    case kScrollLockKeyCode:    raptorKeyCode = nsIDOMKeyEvent::DOM_VK_SCROLL_LOCK;   break;
    case kPrintScreenKeyCode:   raptorKeyCode = nsIDOMKeyEvent::DOM_VK_PRINTSCREEN;   break;

// keypad
    case kKeypad0KeyCode:        raptorKeyCode = nsIDOMKeyEvent::DOM_VK_NUMPAD0;      break;
    case kKeypad1KeyCode:        raptorKeyCode = nsIDOMKeyEvent::DOM_VK_NUMPAD1;      break;
    case kKeypad2KeyCode:        raptorKeyCode = nsIDOMKeyEvent::DOM_VK_NUMPAD2;      break;
    case kKeypad3KeyCode:        raptorKeyCode = nsIDOMKeyEvent::DOM_VK_NUMPAD3;      break;
    case kKeypad4KeyCode:        raptorKeyCode = nsIDOMKeyEvent::DOM_VK_NUMPAD4;      break;
    case kKeypad5KeyCode:        raptorKeyCode = nsIDOMKeyEvent::DOM_VK_NUMPAD5;      break;
    case kKeypad6KeyCode:        raptorKeyCode = nsIDOMKeyEvent::DOM_VK_NUMPAD6;      break;
    case kKeypad7KeyCode:        raptorKeyCode = nsIDOMKeyEvent::DOM_VK_NUMPAD7;      break;
    case kKeypad8KeyCode:        raptorKeyCode = nsIDOMKeyEvent::DOM_VK_NUMPAD8;      break;
    case kKeypad9KeyCode:        raptorKeyCode = nsIDOMKeyEvent::DOM_VK_NUMPAD9;      break;

    case kKeypadMultiplyKeyCode: raptorKeyCode = nsIDOMKeyEvent::DOM_VK_MULTIPLY;     break;
    case kKeypadAddKeyCode:      raptorKeyCode = nsIDOMKeyEvent::DOM_VK_ADD;          break;
    case kKeypadSubtractKeyCode: raptorKeyCode = nsIDOMKeyEvent::DOM_VK_SUBTRACT;     break;
    case kKeypadDecimalKeyCode:  raptorKeyCode = nsIDOMKeyEvent::DOM_VK_DECIMAL;      break;
    case kKeypadDivideKeyCode:   raptorKeyCode = nsIDOMKeyEvent::DOM_VK_DIVIDE;       break;
//  case ??             :        raptorKeyCode = nsIDOMKeyEvent::DOM_VK_SEPARATOR;    break;

// this may clash with vk_insert, but help key is more useful in mozilla
    case kHelpKeyCode:          raptorKeyCode = nsIDOMKeyEvent::DOM_VK_HELP;          break;
    case kDeleteKeyCode:        raptorKeyCode = nsIDOMKeyEvent::DOM_VK_DELETE;        break;
    case kEscapeKeyCode:        raptorKeyCode = nsIDOMKeyEvent::DOM_VK_ESCAPE;        break;
    case kClearKeyCode:         raptorKeyCode = nsIDOMKeyEvent::DOM_VK_CLEAR;         break;

    case kBackspaceKeyCode:     raptorKeyCode = nsIDOMKeyEvent::DOM_VK_BACK_SPACE;    break;
    case kTabKeyCode:           raptorKeyCode = nsIDOMKeyEvent::DOM_VK_TAB;           break;
    case kHomeKeyCode:          raptorKeyCode = nsIDOMKeyEvent::DOM_VK_HOME;          break;
    case kEndKeyCode:           raptorKeyCode = nsIDOMKeyEvent::DOM_VK_END;           break;
    case kPageUpKeyCode:        raptorKeyCode = nsIDOMKeyEvent::DOM_VK_PAGE_UP;       break;
    case kPageDownKeyCode:      raptorKeyCode = nsIDOMKeyEvent::DOM_VK_PAGE_DOWN;     break;
    case kLeftArrowKeyCode:     raptorKeyCode = nsIDOMKeyEvent::DOM_VK_LEFT;          break;
    case kRightArrowKeyCode:    raptorKeyCode = nsIDOMKeyEvent::DOM_VK_RIGHT;         break;
    case kUpArrowKeyCode:       raptorKeyCode = nsIDOMKeyEvent::DOM_VK_UP;            break;
    case kDownArrowKeyCode:     raptorKeyCode = nsIDOMKeyEvent::DOM_VK_DOWN;          break;

		default:
				if ((eventModifiers & controlKey) != 0)
				  charCode += 64;
	  	
				// if we haven't gotten the key code already, look at the char code
				switch (charCode)
				{
          case kReturnCharCode: raptorKeyCode = nsIDOMKeyEvent::DOM_VK_RETURN;       break;
          case kEnterCharCode:  raptorKeyCode = nsIDOMKeyEvent::DOM_VK_RETURN;       break;    // fix me!
          case ' ':             raptorKeyCode = nsIDOMKeyEvent::DOM_VK_SPACE;        break;
          case ';':             raptorKeyCode = nsIDOMKeyEvent::DOM_VK_SEMICOLON;    break;
          case '=':             raptorKeyCode = nsIDOMKeyEvent::DOM_VK_EQUALS;       break;
          case ',':             raptorKeyCode = nsIDOMKeyEvent::DOM_VK_COMMA;        break;
          case '.':             raptorKeyCode = nsIDOMKeyEvent::DOM_VK_PERIOD;       break;
          case '/':             raptorKeyCode = nsIDOMKeyEvent::DOM_VK_SLASH;        break;
          case '`':             raptorKeyCode = nsIDOMKeyEvent::DOM_VK_BACK_QUOTE;   break;
          case '{':
          case '[':             raptorKeyCode = nsIDOMKeyEvent::DOM_VK_OPEN_BRACKET; break;
          case '\\':            raptorKeyCode = nsIDOMKeyEvent::DOM_VK_BACK_SLASH;   break;
          case '}':
          case ']':             raptorKeyCode = nsIDOMKeyEvent::DOM_VK_CLOSE_BRACKET; break;
          case '\'':
          case '"':             raptorKeyCode = nsIDOMKeyEvent::DOM_VK_QUOTE;        break;

					default:
						
						if (charCode >= '0' && charCode <= '9')		// numerals
						{
							raptorKeyCode = charCode;
						}
						else if (charCode >= 'a' && charCode <= 'z')		// lowercase
						{
							raptorKeyCode = toupper(charCode);
						}
						else if (charCode >= 'A' && charCode <= 'Z')		// uppercase
						{
							raptorKeyCode = charCode;
						}

						break;
				}
	}

	return raptorKeyCode;
}


//-------------------------------------------------------------------------
//
// InitializeKeyEvent
//
//-------------------------------------------------------------------------

void nsMacEventHandler::InitializeKeyEvent(nsKeyEvent& aKeyEvent, 
    EventRecord& aOSEvent, nsWindow* aFocusedWidget, PRUint32 aMessage,
    PRBool aConvertChar)
{
	//
	// initalize the basic message parts
	//
	aKeyEvent.time				= PR_IntervalNow();
	
	//
	// initalize the GUI event parts
	//
  aKeyEvent.widget = aFocusedWidget;
	aKeyEvent.nativeMsg		= (void*)&aOSEvent;
	
	//
	// nsInputEvent parts
	//
	aKeyEvent.isShift			= ((aOSEvent.modifiers & shiftKey) != 0);
	aKeyEvent.isControl		= ((aOSEvent.modifiers & controlKey) != 0);
	aKeyEvent.isAlt				= ((aOSEvent.modifiers & optionKey) != 0);
	aKeyEvent.isMeta		= ((aOSEvent.modifiers & cmdKey) != 0);
	
	//
	// nsKeyEvent parts
	//
  if (aMessage == NS_KEY_PRESS 
		&& !IsSpecialRaptorKey((aOSEvent.message & keyCodeMask) >> 8) )
	{
    if (aKeyEvent.isControl)
		{
      if (aConvertChar) 
      {
			aKeyEvent.charCode = (aOSEvent.message & charCodeMask);
        if (aKeyEvent.charCode <= 26)
			{
          if (aKeyEvent.isShift)
					aKeyEvent.charCode += 'A' - 1;
				else
					aKeyEvent.charCode += 'a' - 1;
        } // if (aKeyEvent.charCode <= 26)
      }
			aKeyEvent.keyCode	= 0;
    } // if (aKeyEvent.isControl)
    else // else for if (aKeyEvent.isControl)
		{
      if (!aKeyEvent.isMeta)
			{
				aKeyEvent.isControl = aKeyEvent.isAlt = aKeyEvent.isMeta = 0;
      } // if (!aKeyEvent.isMeta)
    
			aKeyEvent.keyCode	= 0;
      if (aConvertChar) 
      {
			aKeyEvent.charCode = ConvertKeyEventToUnicode(aOSEvent);
        if (aKeyEvent.isShift && aKeyEvent.charCode <= 'z' && aKeyEvent.charCode >= 'a') 
        {
			  aKeyEvent.charCode -= 32;
			}
			NS_ASSERTION(0 != aKeyEvent.charCode, "nsMacEventHandler::InitializeKeyEvent: ConvertKeyEventToUnicode returned 0.");
      }
    } // else for if (aKeyEvent.isControl)
	} // if (message == NS_KEY_PRESS && !IsSpecialRaptorKey((aOSEvent.message & keyCodeMask) >> 8) )
	else
	{
		aKeyEvent.keyCode = ConvertMacToRaptorKeyCode(aOSEvent.message & charCodeMask, (aOSEvent.message & keyCodeMask) >> 8, aOSEvent.modifiers);
		aKeyEvent.charCode = 0;
	} // else for  if (message == NS_KEY_PRESS && !IsSpecialRaptorKey((aOSEvent.message & keyCodeMask) >> 8) )
  
  //
  // obscure cursor if appropriate
  //
  if (aMessage == NS_KEY_PRESS 
		&& !aKeyEvent.isMeta 
		&& aKeyEvent.keyCode != nsIDOMKeyEvent::DOM_VK_PAGE_UP 
		&& aKeyEvent.keyCode != nsIDOMKeyEvent::DOM_VK_PAGE_DOWN
		// also consider:  function keys and sole modifier keys
	) 
	{
    ::ObscureCursor();
  }
}



//-------------------------------------------------------------------------
//
// IsSpecialRaptorKey
//
//-------------------------------------------------------------------------

PRBool nsMacEventHandler::IsSpecialRaptorKey(UInt32 macKeyCode)
{
	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:				isSpecial = PR_TRUE; break;
		case kShiftKeyCode:					isSpecial = PR_TRUE; break;
		case kCommandKeyCode:       isSpecial = PR_TRUE; break;
		case kCapsLockKeyCode:			isSpecial = PR_TRUE; break;
		case kControlKeyCode:				isSpecial = PR_TRUE; break;
		case kOptionkeyCode:				isSpecial = PR_TRUE; break;
		case kClearKeyCode:					isSpecial = PR_TRUE; break;

// function keys
		case kF1KeyCode:					isSpecial = PR_TRUE; break;
		case kF2KeyCode:					isSpecial = PR_TRUE; break;
		case kF3KeyCode:					isSpecial = PR_TRUE; break;
		case kF4KeyCode:					isSpecial = PR_TRUE; break;
		case kF5KeyCode:					isSpecial = PR_TRUE; break;
		case kF6KeyCode:					isSpecial = PR_TRUE; break;
		case kF7KeyCode:					isSpecial = PR_TRUE; break;
		case kF8KeyCode:					isSpecial = PR_TRUE; break;
		case kF9KeyCode:					isSpecial = PR_TRUE; break;
		case kF10KeyCode:					isSpecial = PR_TRUE; break;
		case kF11KeyCode:					isSpecial = PR_TRUE; break;
		case kF12KeyCode:					isSpecial = PR_TRUE; break;
		case kPauseKeyCode:				isSpecial = PR_TRUE; break;
		case kScrollLockKeyCode:	isSpecial = PR_TRUE; break;
		case kPrintScreenKeyCode:	isSpecial = PR_TRUE; break;

		case kHelpKeyCode:					isSpecial = PR_TRUE; break;
		case kDeleteKeyCode:				isSpecial = PR_TRUE; break;
		case kTabKeyCode:						isSpecial = PR_TRUE; break;
		case kBackspaceKeyCode:     isSpecial = PR_TRUE; break;

		case kHomeKeyCode:					isSpecial = PR_TRUE; break;	
		case kEndKeyCode:						isSpecial = PR_TRUE; break;
		case kPageUpKeyCode:				isSpecial = PR_TRUE; break;
		case kPageDownKeyCode:			isSpecial = PR_TRUE; break;
		case kLeftArrowKeyCode:			isSpecial = PR_TRUE; break;
		case kRightArrowKeyCode:		isSpecial = PR_TRUE; break;
		case kUpArrowKeyCode:				isSpecial = PR_TRUE; break;
		case kDownArrowKeyCode:			isSpecial = PR_TRUE; break;
		case kReturnKeyCode:        isSpecial = PR_TRUE; break;
		case kEnterKeyCode:         isSpecial = PR_TRUE; break;
		case kPowerbookEnterKeyCode: isSpecial = PR_TRUE; break;

		default:							isSpecial = PR_FALSE; break;
	}
	return isSpecial;
}


//-------------------------------------------------------------------------
//
// ConvertKeyEventToUnicode
//
//-------------------------------------------------------------------------
// we currently set the following to 5. We should fix this function later...
#define UNICODE_BUFFER_SIZE_FOR_KEY 5
PRUint32 nsMacEventHandler::ConvertKeyEventToUnicode(EventRecord& aOSEvent)
{
	char charResult = aOSEvent.message & charCodeMask;
	
	//
	// get the script of text for Unicode conversion
	//
	ScriptCode textScript = (ScriptCode)GetScriptManagerVariable(smKeyScript);

	//
	// convert our script code (smKeyScript) to a TextEncoding 
	//
	TextEncoding textEncodingFromScript;
	OSErr err = ::UpgradeScriptInfoToTextEncoding(textScript, kTextLanguageDontCare, kTextRegionDontCare, nsnull,
											&textEncodingFromScript);
	NS_ASSERTION(err == noErr, "nsMacEventHandler::ConvertKeyEventToUnicode: UpgradeScriptInfoToTextEncoding failed.");
	if (err != noErr) return 0;
	
	TextToUnicodeInfo	textToUnicodeInfo;
	err = ::CreateTextToUnicodeInfoByEncoding(textEncodingFromScript,&textToUnicodeInfo);
	NS_ASSERTION(err == noErr, "nsMacEventHandler::ConvertKeyEventToUnicode: CreateUnicodeToTextInfoByEncoding failed.");
	if (err != noErr) return 0;

	//
	// convert to Unicode
	//
	ByteCount result_size, source_read;
	PRUnichar unicharResult[UNICODE_BUFFER_SIZE_FOR_KEY];
	err = ::ConvertFromTextToUnicode(textToUnicodeInfo,
									sizeof(char),&charResult,
									kUnicodeLooseMappingsMask,
									0,NULL,NULL,NULL,
									sizeof(PRUnichar)*UNICODE_BUFFER_SIZE_FOR_KEY,&source_read,
									&result_size,NS_REINTERPRET_CAST(PRUint16*, unicharResult));
	::DisposeTextToUnicodeInfo(&textToUnicodeInfo);
	NS_ASSERTION(err == noErr, "nsMacEventHandler::ConvertKeyEventToUnicode: ConverFromTextToUnicode failed.");
	// if we got the following result, then it mean we got more than one Unichar from it.
	NS_ASSERTION(result_size == 2, "nsMacEventHandler::ConvertKeyEventToUnicode: ConverFromTextToUnicode failed.");
	// Fix Me!!!
	// the result_size will not equal to 2 in the following cases:
	//
	// 1. Hebrew/Arabic scripts, when we convert ( ) { } [ ]  < >  etc. 
	// The TEC will produce result_size = 6 (3 PRUnichar*) :
	// one Right-To-Left-Mark, the char and one Left-To-Right-Mark
	// We should fix this later...
	// See the following URL for the details...
	// ftp://ftp.unicode.org/Public/MAPPINGS/VENDORS/APPLE/ARABIC.TXT
	// ftp://ftp.unicode.org/Public/MAPPINGS/VENDORS/APPLE/FARSI.TXT
	// ftp://ftp.unicode.org/Public/MAPPINGS/VENDORS/APPLE/HEBREW.TXT
	//
	// 2. Also, it probably won't work in Thai and Indic keyboard since one char may convert to
	// several PRUnichar. It sometimes add zwj or zwnj. See the following url for details.
	// ftp://ftp.unicode.org/Public/MAPPINGS/VENDORS/APPLE/DEVANAGA.TXT
	// ftp://ftp.unicode.org/Public/MAPPINGS/VENDORS/APPLE/GUJARATI.TXT
	// ftp://ftp.unicode.org/Public/MAPPINGS/VENDORS/APPLE/GURMUKHI.TXT
	// ftp://ftp.unicode.org/Public/MAPPINGS/VENDORS/APPLE/THAI.TXT
	
//	if (err != noErr) return 0;
// I think we should ignore the above error since we already have the result we want

	return unicharResult[0];
}


//
// ConvertKeyEventToContextMenuEvent
//
// Take a key event and all of its attributes at convert it into
// a context menu event. We want just about everything (focused
// widget, etc) but a few things need to bt tweaked.
//
static void
ConvertKeyEventToContextMenuEvent(const nsKeyEvent* inKeyEvent, nsMouseEvent* outCMEvent)
{
  *(nsInputEvent*)outCMEvent = *(nsInputEvent*)inKeyEvent;
  
  outCMEvent->eventStructType = NS_MOUSE_EVENT;
  outCMEvent->message = NS_CONTEXTMENU_KEY;
  outCMEvent->isShift = outCMEvent->isControl = outCMEvent->isAlt = outCMEvent->isMeta = PR_FALSE;
  
  outCMEvent->clickCount = 0;
  outCMEvent->acceptActivation = PR_FALSE;
}


//
// IsContextMenuKey
//
// Check if the event should be a context menu event instead. Currently,
// that is a control-space.
//
static inline PRBool
IsContextMenuKey(const nsKeyEvent& inKeyEvent)
{
  enum { kContextMenuKey = ' ' } ;

  return ( inKeyEvent.charCode == kContextMenuKey && inKeyEvent.isControl &&
            !inKeyEvent.isShift && !inKeyEvent.isMeta && !inKeyEvent.isAlt );
}


//-------------------------------------------------------------------------
//
// HandleUKeyEvent
//
//-------------------------------------------------------------------------
PRBool nsMacEventHandler::HandleUKeyEvent(const PRUnichar* text, long charCount, EventRecord& aOSEvent)
{
  ClearLastMouseUp();

  // The focused widget changed in HandleKeyUpDownEvent, so no NS_KEY_PRESS
  // events should be generated.
  if (mKeyIgnore)
    return PR_FALSE;

  PRBool handled = PR_FALSE;

  // get the focused widget
  nsWindow* focusedWidget = mEventDispatchHandler->GetActive();
  if (!focusedWidget)
    focusedWidget = mTopLevelWidget;
  
  // simulate key press events
  if (!IsSpecialRaptorKey((aOSEvent.message & keyCodeMask) >> 8))
  {
    // it is a message with text, send all the unicode characters
    PRInt32 i;
    for (i = 0; i < charCount; i++)
    {
      nsKeyEvent keyPressEvent(PR_TRUE, NS_KEY_PRESS, nsnull);
      InitializeKeyEvent(keyPressEvent, aOSEvent, focusedWidget, NS_KEY_PRESS, PR_FALSE);
      keyPressEvent.charCode = text[i];

      // If keydown default was prevented, do same for keypress
      if (mKeyHandled)
        keyPressEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT;

      // control key is special in that it doesn't give us letters
      // it generates a charcode of 0x01 for control-a
      // so we offset to do the right thing for gecko
      // this doesn't happen for us in InitializeKeyEvent because we pass
      // PR_FALSE so no character translation occurs.
      // I'm guessing we don't want to do the translation there because
      // translation already occurred for the string passed to this method.
      if (keyPressEvent.isControl && keyPressEvent.charCode <= 26)       
      {
        if (keyPressEvent.isShift)
          keyPressEvent.charCode += 'A' - 1;
        else
          keyPressEvent.charCode += 'a' - 1;
      }

      // this block of code is triggered when user presses
      // a combination such as command-shift-M
      if (keyPressEvent.isShift && keyPressEvent.charCode <= 'z' && keyPressEvent.charCode >= 'a') 
        keyPressEvent.charCode -= 32;

      // before we dispatch a key, check if it's the context menu key.
      // If so, send a context menu event instead.
      if ( IsContextMenuKey(keyPressEvent) ) {
        nsMouseEvent contextMenuEvent(PR_TRUE, 0, nsnull, nsMouseEvent::eReal);
        ConvertKeyEventToContextMenuEvent(&keyPressEvent, &contextMenuEvent);
        handled |= focusedWidget->DispatchWindowEvent(contextMenuEvent);
      }
      else {
        // Send ordinary keypresses
        handled |= focusedWidget->DispatchWindowEvent(keyPressEvent);
      }
    }
  }
  else {
    // "Special" keys, only send one event based on the keycode
    nsKeyEvent keyPressEvent(PR_TRUE, NS_KEY_PRESS, nsnull);
    InitializeKeyEvent(keyPressEvent, aOSEvent, focusedWidget, NS_KEY_PRESS, PR_FALSE);
    handled = focusedWidget->DispatchWindowEvent(keyPressEvent);
  }
  return handled;
}

#pragma mark -
//-------------------------------------------------------------------------
//
// HandleActivateEvent
//
//-------------------------------------------------------------------------
void nsMacEventHandler::HandleActivateEvent(EventRef aEvent)
{
  ClearLastMouseUp();

  OSErr err;
  PRUint32 eventKind = ::GetEventKind(aEvent);
  PRBool isActive = (eventKind == kEventWindowActivated) ? PR_TRUE : PR_FALSE;

  // Be paranoid about deactivating the last-active window before activating
  // the new one, and about not handling activation for the already-active
  // window.

	if (isActive && sLastActive != this)
	{
		if (sLastActive) {
			WindowRef oldWindow = NS_STATIC_CAST(WindowRef,
			 sLastActive->mTopLevelWidget->GetNativeData(NS_NATIVE_DISPLAY));
			// Deactivating also causes HandleActivateEvent to
			// be called on sLastActive with isActive = PR_FALSE,
			// so sLastActive will be nsnull after this call.
			::ActivateWindow(oldWindow, PR_FALSE);
		}

		sLastActive = this;

		//
		// Activate The TSMDocument associated with this handler
		//
		if (mTSMDocument) {
		  // make sure we do not use input widnow even some other code turn it for default by calling 
		  // ::UseInputWindow(nsnull, TRUE); 
		  ::UseInputWindow(mTSMDocument, FALSE); 
		  err = ::ActivateTSMDocument(mTSMDocument);
		}

#ifdef DEBUG_TSM
#if 0
		NS_ASSERTION(err==noErr,"nsMacEventHandler::HandleActivateEvent: ActivateTSMDocument failed");
#endif
		printf("nsEventHandler::HandleActivateEvent: ActivateTSMDocument[%p] %s return %d\n",mTSMDocument,
		(err==noErr)?"":"ERROR", err);
#endif
		
		
		PRBool active;
		mTopLevelWidget->IsActive(&active);
		nsWindow*	focusedWidget = mTopLevelWidget;
		if (!active) {
		  mEventDispatchHandler->SetActivated(focusedWidget);
		  mTopLevelWidget->SetIsActive(PR_TRUE);
		}
		else if (!mEventDispatchHandler->GetActive()) {
		  NS_ASSERTION(0, "We think we're active, but there is no active widget!");
		  mEventDispatchHandler->SetActivated(focusedWidget);
		}
		
		// Twiddle menu bars
		nsIMenuBar* menuBar = focusedWidget->GetMenuBar();
		if (menuBar)
		{
		  menuBar->Paint();
		}
		else
		{
		  //ÄTODO:	if the focusedWidget doesn't have a menubar,
		  //				look all the way up to the window
		  //				until one of the parents has a menubar
		}

		// Mouse-moved events were not processed while the window was
		// inactive.  Grab the current mouse position and treat it as
		// a mouse-moved event would be.
		EventRecord eventRecord;
		::ConvertEventRefToEventRecord(aEvent, &eventRecord);
		HandleMouseMoveEvent(eventRecord);
	}
	else if (!isActive && sLastActive == this)
	{
		sLastActive = nsnull;

		if (nsnull != gRollupListener && (nsnull != gRollupWidget) ) {
			// If there's a widget to be rolled up, it's got to
			// be attached to the active window, so it's OK to
			// roll it up on any deactivate event without
			// further checking.
			gRollupListener->Rollup();
		}
		//
		// Deactivate the TSMDocument assoicated with this EventHandler
		//
		if (mTSMDocument) {
		  // We should call FixTSMDocument() before deactivate the window.
		  // see http://bugzilla.mozilla.gr.jp/show_bug.cgi?id=4135
		  ResetInputState();
		  // make sure we do not use input widnow even some other code turn it for default by calling 
		  // ::UseInputWindow(nsnull, TRUE); 
		  ::UseInputWindow(mTSMDocument, FALSE); 
		  err = ::DeactivateTSMDocument(mTSMDocument);
		}

#ifdef DEBUG_TSM
		NS_ASSERTION((noErr==err)||(tsmDocNotActiveErr==err),"nsMacEventHandler::HandleActivateEvent: DeactivateTSMDocument failed");
		printf("nsEventHandler::HandleActivateEvent: DeactivateTSMDocument[%p] %s return %d\n",mTSMDocument,
		(err==noErr)?"":"ERROR", err);
#endif
		// Dispatch an NS_DEACTIVATE event 
		mEventDispatchHandler->SetDeactivated(mTopLevelWidget);
		mTopLevelWidget->SetIsActive(PR_FALSE);
	}
}


//-------------------------------------------------------------------------
//
// ResizeEvent
//
//-------------------------------------------------------------------------
PRBool nsMacEventHandler::ResizeEvent ( WindowRef inWindow )
{
	Rect macRect;
	::GetWindowPortBounds ( inWindow, &macRect );
	::LocalToGlobal(&topLeft(macRect));
	::LocalToGlobal(&botRight(macRect));
	mTopLevelWidget->Resize(macRect.right - macRect.left,
                                macRect.bottom - macRect.top,
                                PR_FALSE,
                                PR_TRUE); // resize came from the UI via event
	if (nsnull != gRollupListener && (nsnull != gRollupWidget) )
		gRollupListener->Rollup();
	mTopLevelWidget->UserStateForResize(); // size a zoomed window and it's no longer zoomed

	return PR_TRUE;
}


//
// Scroll
//
// Called from a mouseWheel carbon event, tell Gecko to scroll.
// 
PRBool
nsMacEventHandler::Scroll(PRInt32 aDeltaY, PRInt32 aDeltaX,
                          PRBool aIsPixels, const Point& aMouseLoc,
                          nsWindow* aWindow, PRUint32 aModifiers) {
  PRBool resY = ScrollAxis(nsMouseScrollEvent::kIsVertical, aDeltaY,
                           aIsPixels, aMouseLoc, aWindow, aModifiers);
  PRBool resX = ScrollAxis(nsMouseScrollEvent::kIsHorizontal, aDeltaX,
                           aIsPixels, aMouseLoc, aWindow, aModifiers);

  return resY || resX;
} // Scroll


//
// ScrollAxis
//
PRBool
nsMacEventHandler::ScrollAxis(nsMouseScrollEvent::nsMouseScrollFlags aAxis,
                              PRInt32 aDelta, PRBool aIsPixels,
                              const Point& aMouseLoc, nsWindow* aWindow,
                              PRUint32 aModifiers)
{
  // Only scroll active windows.  Treat popups as active.
  WindowRef windowRef = NS_STATIC_CAST(WindowRef,
                         mTopLevelWidget->GetNativeData(NS_NATIVE_DISPLAY));
  nsWindowType windowType;
  mTopLevelWidget->GetWindowType(windowType);
  if (!::IsWindowActive(windowRef) && windowType != eWindowType_popup)
    return PR_FALSE;

  // Figure out which widget should be scrolled by traversing the widget
  // hierarchy beginning at the root nsWindow.  aMouseLoc should be
  // relative to the origin of this nsWindow.  If the scroll event came
  // from an nsMacWindow, then aWindow should refer to that nsMacWindow.
  nsIWidget* widgetToScroll = aWindow->FindWidgetHit(aMouseLoc);

  // Not all scroll events for the window are over a widget.  Consider
  // the title bar.
  if (!widgetToScroll)
    return PR_FALSE;

  if (aDelta == 0) {
    // Don't need to do anything, but eat the event anyway.
    return PR_TRUE;
  }

  if (gRollupListener && gRollupWidget) {
    // Roll up the rollup widget if the scroll isn't targeted at it
    // (or one of its children) and the listener was told to do so.

    PRBool rollup = PR_FALSE;
    gRollupListener->ShouldRollupOnMouseWheelEvent(&rollup);

    if (rollup) {
      nsCOMPtr<nsIWidget> widgetOrAncestor = widgetToScroll;
      do {
        if (widgetOrAncestor == gRollupWidget) {
          rollup = PR_FALSE;
          break;
        }
      } while (widgetOrAncestor = widgetOrAncestor->GetParent());
    }

    if (rollup)
      gRollupListener->Rollup();
  }

  nsMouseScrollEvent scrollEvent(PR_TRUE, NS_MOUSE_SCROLL, widgetToScroll);

  // The direction we get from the carbon event is opposite from the way
  // mozilla looks at it.  Reverse the direction.
  scrollEvent.delta = -aDelta;

  // If the scroll event comes from a mouse that only has a scroll wheel for
  // the vertical axis, and the shift key is held down, the system presents
  // it as a horizontal scroll and doesn't clear the shift key bit from
  // aModifiers.  The Mac is supposed to scroll horizontally in such a case.
  //
  // If the scroll event comes from a mouse that can scroll both axes, the
  // system doesn't apply any of this shift-key fixery.
  scrollEvent.scrollFlags = aAxis;

  if (aIsPixels)
    scrollEvent.scrollFlags |= nsMouseScrollEvent::kIsPixels;

  // convert window-relative (local) mouse coordinates to widget-relative
  // coords for Gecko.
  nsPoint mouseLocRelativeToWidget(aMouseLoc.h, aMouseLoc.v);
  nsRect bounds;
  widgetToScroll->GetBounds(bounds);
  nsPoint widgetOrigin(bounds.x, bounds.y);
  widgetToScroll->ConvertToDeviceCoordinates(widgetOrigin.x, widgetOrigin.y);
  mouseLocRelativeToWidget.MoveBy(-widgetOrigin.x, -widgetOrigin.y);

  scrollEvent.point.x = mouseLocRelativeToWidget.x;
  scrollEvent.point.y = mouseLocRelativeToWidget.y;
  scrollEvent.time = PR_IntervalNow();

  // Translate OS event modifiers into Gecko event modifiers
  scrollEvent.isShift   = ((aModifiers & shiftKey)   != 0);
  scrollEvent.isControl = ((aModifiers & controlKey) != 0);
  scrollEvent.isAlt     = ((aModifiers & optionKey)  != 0);
  scrollEvent.isMeta    = ((aModifiers & cmdKey)     != 0);

  nsEventStatus status;
  widgetToScroll->DispatchEvent(&scrollEvent, status);

  return nsWindow::ConvertStatus(status);
} // ScrollAxis


//-------------------------------------------------------------------------
//
// HandleMouseDownEvent
//
//-------------------------------------------------------------------------
PRBool nsMacEventHandler::HandleMouseDownEvent(EventRecord&	aOSEvent)
{    
	PRBool retVal = PR_FALSE;

	WindowPtr		whichWindow;
	short partCode = ::FindWindow(aOSEvent.where, &whichWindow);

  PRBool ignoreClickInContent = PR_FALSE;

  // Deal with any popups (comboboxes, xul popups, XP menus, etc) that might have to 
  // be closed down since they are implemented as stand-alone windows on top of
  // the current content window. If the click is not in the front window, we need to
  // hide the popup if one is visible. Furthermore, we want to ignore the click that
  // caused the popup to close and not pass it along to gecko.
  if ( whichWindow != ::FrontWindow() ) {
    if ( gRollupListener && gRollupWidget ) {
      PRBool rollup = PR_TRUE;

      // if we're dealing with menus, we probably have submenus and we don't want
      // to rollup if the click is in a parent menu of the current submenu.
      nsCOMPtr<nsIMenuRollup> menuRollup ( do_QueryInterface(gRollupListener) );
      if ( menuRollup ) {
        nsCOMPtr<nsISupportsArray> widgetChain;
        menuRollup->GetSubmenuWidgetChain ( getter_AddRefs(widgetChain) );
        if ( widgetChain ) {      
          PRUint32 count = 0;
          widgetChain->Count(&count);
          for ( PRUint32 i = 0; i < count; ++i ) {
            nsCOMPtr<nsISupports> genericWidget;
            widgetChain->GetElementAt ( i, getter_AddRefs(genericWidget) );
            nsCOMPtr<nsIWidget> widget ( do_QueryInterface(genericWidget) );
            if ( widget ) {
              if ( NS_REINTERPRET_CAST(WindowPtr,widget->GetNativeData(NS_NATIVE_DISPLAY)) == whichWindow )
                 rollup = PR_FALSE;
            }         
          } // foreach parent menu widget
        }
      } // if rollup listener knows about menus
      
      // if we've determined that we should still rollup everything, do it.
      if ( rollup ) {
  			gRollupListener->Rollup();
  			ignoreClickInContent = PR_TRUE;
  	  }
 
		} // if a popup is active
  } // if click in a window not the frontmost
 
	switch (partCode)
	{
		case inDrag:
		{
			Point macPoint;
			Rect portRect;
			::GetWindowPortBounds(whichWindow, &portRect);
			macPoint = topLeft(portRect);
			::LocalToGlobal(&macPoint);
			mTopLevelWidget->MoveToGlobalPoint(macPoint.h, macPoint.v);
			retVal = PR_TRUE;
			break;
		}

		case inGrow:
		{
      ResizeEvent ( whichWindow );
      retVal = PR_TRUE;
      break;
		}

		case inGoAway:
		{
			ResetInputState();	// IM:TEXT 7-23 said we need to call FixTSMDocument when we go away...
			if (nsnull != gRollupListener && (nsnull != gRollupWidget) ) {
				gRollupListener->Rollup();
			}
			mEventDispatchHandler->DispatchGuiEvent(mTopLevelWidget, NS_XUL_CLOSE);		
			// mTopLevelWidget->Destroy(); (this, by contrast, would immediately close the window)
			retVal = PR_TRUE;
			break;
		}

		case inContent:
		{
			// don't allow clicks that rolled up a popup through to the content area.
			if ( ignoreClickInContent )
				break;
						
			nsMouseEvent mouseEvent(PR_TRUE, 0, nsnull, nsMouseEvent::eReal);
			PRUint32 mouseButton = NS_MOUSE_LEFT_BUTTON_DOWN;
			if ( aOSEvent.modifiers & controlKey )
			  mouseButton = NS_MOUSE_RIGHT_BUTTON_DOWN;

			// We've hacked our events to include the button.
			// Normally message is undefined in mouse click/drag events.
			if ( aOSEvent.message == kEventMouseButtonSecondary )
			  mouseButton = NS_MOUSE_RIGHT_BUTTON_DOWN;
			if ( aOSEvent.message == kEventMouseButtonTertiary )
			  mouseButton = NS_MOUSE_MIDDLE_BUTTON_DOWN;

			ConvertOSEventToMouseEvent(aOSEvent, mouseEvent, mouseButton);

			nsCOMPtr<nsIWidget> kungFuDeathGrip ( mouseEvent.widget );            // ensure widget doesn't go away
			nsWindow* widgetHit = NS_STATIC_CAST(nsWindow*, mouseEvent.widget);   //   while we're processing event
			if (widgetHit)
			{        
				// set the activation and focus on the widget hit, if it accepts it
				{
					nsMouseEvent mouseActivateEvent(PR_TRUE, 0, nsnull,
                                          nsMouseEvent::eReal);
          ConvertOSEventToMouseEvent(aOSEvent, mouseActivateEvent, NS_MOUSE_ACTIVATE);
					widgetHit->DispatchMouseEvent(mouseActivateEvent);
				}

				// dispatch the event
				retVal = widgetHit->DispatchMouseEvent(mouseEvent);
				
				// if we're a control-click, send in an additional NS_CONTEXTMENU event
				// after the mouse down.
				if ( mouseButton == NS_MOUSE_RIGHT_BUTTON_DOWN ) {
    			nsMouseEvent contextMenuEvent(PR_TRUE, 0, nsnull,
                                        nsMouseEvent::eReal);
    			ConvertOSEventToMouseEvent(aOSEvent, contextMenuEvent, NS_CONTEXTMENU);
    			contextMenuEvent.isControl = PR_FALSE;    			
					widgetHit->DispatchMouseEvent(contextMenuEvent);
        } 

        // If we found a widget to dispatch to, say we handled the event.
        // The meaning of the result of DispatchMouseEvent() is ambiguous.
        // In Gecko terms, it means "continue processing", but that doesn't
        // say if the event was really handled (which is a simplistic notion
        // to Gecko).
        retVal = PR_TRUE;
			}
						
			mEventDispatchHandler->SetWidgetHit(widgetHit);
			mMouseInWidgetHit = PR_TRUE;
			break;
		}


		case inZoomIn:
		case inZoomOut:
		{
			mEventDispatchHandler->DispatchSizeModeEvent(mTopLevelWidget,
				partCode == inZoomIn ? nsSizeMode_Normal : nsSizeMode_Maximized);
			retVal = PR_TRUE;
			break;
		}

    case inToolbarButton:           // we get this part on Mac OS X only
      mEventDispatchHandler->DispatchGuiEvent(mTopLevelWidget, NS_OS_TOOLBAR);		
      retVal = PR_TRUE;
      break;
	}
	return retVal;
}


//-------------------------------------------------------------------------
//
// HandleMouseUpEvent
//
//-------------------------------------------------------------------------
PRBool nsMacEventHandler::HandleMouseUpEvent(
														EventRecord&		aOSEvent)
{
	PRBool retVal = PR_FALSE;

	nsMouseEvent mouseEvent(PR_TRUE, 0, nsnull, nsMouseEvent::eReal);
	PRUint32 mouseButton = NS_MOUSE_LEFT_BUTTON_UP;

	// We've hacked our events to include the button.
	// Normally message is undefined in mouse click/drag events.
	if ( aOSEvent.message == kEventMouseButtonSecondary )
		mouseButton = NS_MOUSE_RIGHT_BUTTON_UP;
	if ( aOSEvent.message == kEventMouseButtonTertiary )
		mouseButton = NS_MOUSE_MIDDLE_BUTTON_UP;

	ConvertOSEventToMouseEvent(aOSEvent, mouseEvent, mouseButton);

	nsWindow* widgetReleased = (nsWindow*)mouseEvent.widget;
	nsWindow* widgetHit = mEventDispatchHandler->GetWidgetHit();

	if ( widgetReleased )
	{
		widgetReleased->DispatchMouseEvent(mouseEvent);
		// If we found a widget to dispatch the event to, say that we handled it
		// (see comments in HandleMouseDownEvent()).
		retVal = PR_TRUE;
	}
	
	if ( widgetReleased != widgetHit ) {
	  //XXX we should send a mouse exit event to the last widget, right?!?! But
	  //XXX we cannot use the same event, because the coordinates we just
	  //XXX computed are in the wrong window/widget coordinate space. I'm 
	  //XXX unclear what we should do in this case. (pinkerton).
	}
	
	mEventDispatchHandler->SetWidgetHit(nsnull);

	return retVal;
}


//-------------------------------------------------------------------------
//
// HandleMouseMoveEvent
//
//-------------------------------------------------------------------------
PRBool nsMacEventHandler::HandleMouseMoveEvent( EventRecord& aOSEvent )
{
	nsWindow* lastWidgetHit = mEventDispatchHandler->GetWidgetHit();
	nsWindow* lastWidgetPointed = mEventDispatchHandler->GetWidgetPointed();
  
	PRBool retVal = PR_FALSE;

	WindowRef wind = reinterpret_cast<WindowRef>(mTopLevelWidget->GetNativeData(NS_NATIVE_DISPLAY));
	nsWindowType windowType;
	mTopLevelWidget->GetWindowType(windowType);
	if (!::IsWindowActive(wind) && windowType != eWindowType_popup)
		return retVal;

	nsMouseEvent mouseEvent(PR_TRUE, 0, nsnull, nsMouseEvent::eReal);
	ConvertOSEventToMouseEvent(aOSEvent, mouseEvent, NS_MOUSE_MOVE);
	if (lastWidgetHit)
	{
		Point macPoint = aOSEvent.where;
		nsGraphicsUtils::SafeSetPortWindowPort(wind);
		{
			StOriginSetter  originSetter(wind);
			::GlobalToLocal(&macPoint);
		}
		PRBool inWidgetHit = lastWidgetHit->PointInWidget(macPoint);
		if (mMouseInWidgetHit != inWidgetHit)
		{
			mMouseInWidgetHit = inWidgetHit;
			mouseEvent.message = (inWidgetHit ? NS_MOUSE_ENTER : NS_MOUSE_EXIT);
		}
		lastWidgetHit->DispatchMouseEvent(mouseEvent);
		retVal = PR_TRUE;
	}
	else
	{
		nsWindow* widgetPointed = (nsWindow*)mouseEvent.widget;
		nsCOMPtr<nsIWidget> kungFuDeathGrip(widgetPointed);     // Protect during processing
		if (widgetPointed != lastWidgetPointed)
		{
			if (lastWidgetPointed)
			{
        // We need to convert the coords to be relative to lastWidgetPointed.
        nsPoint widgetHitPoint = mouseEvent.point;

        Point macPoint = aOSEvent.where;
        nsGraphicsUtils::SafeSetPortWindowPort(wind);

        {
          StOriginSetter originSetter(wind);
          ::GlobalToLocal(&macPoint);
        }

        nsPoint lastWidgetHitPoint(macPoint.h, macPoint.v);

          nsRect bounds;
          lastWidgetPointed->GetBounds(bounds);
          nsPoint widgetOrigin(bounds.x, bounds.y);
          lastWidgetPointed->LocalToWindowCoordinate(widgetOrigin);
          lastWidgetHitPoint.MoveBy(-widgetOrigin.x, -widgetOrigin.y);
				mouseEvent.widget = lastWidgetPointed;
				mouseEvent.point = lastWidgetHitPoint;
				mouseEvent.message = NS_MOUSE_EXIT;
				lastWidgetPointed->DispatchMouseEvent(mouseEvent);
				retVal = PR_TRUE;

				mouseEvent.point = widgetHitPoint;
			}

      mEventDispatchHandler->SetWidgetPointed(widgetPointed);
#if TRACK_MOUSE_LOC
      mEventDispatchHandler->SetGlobalPoint(aOSEvent.where);
#endif

			if (widgetPointed)
			{
				mouseEvent.widget = widgetPointed;
				mouseEvent.message = NS_MOUSE_ENTER;
				widgetPointed->DispatchMouseEvent(mouseEvent);
				retVal = PR_TRUE;
			}
		}
		else
		{
			if (widgetPointed)
			{
				widgetPointed->DispatchMouseEvent(mouseEvent);
				retVal = PR_TRUE;
			}
		}
	}

	return retVal;
}


#pragma mark -
//-------------------------------------------------------------------------
//
// ConvertOSEventToMouseEvent
//
//-------------------------------------------------------------------------
void nsMacEventHandler::ConvertOSEventToMouseEvent(
														EventRecord&		aOSEvent,
														nsMouseEvent&		aMouseEvent,
														PRUint32				aMessage)
{
	// we're going to time double-clicks from mouse *up* to next mouse *down*
	if (aMessage == NS_MOUSE_LEFT_BUTTON_UP  ||
      aMessage == NS_MOUSE_RIGHT_BUTTON_UP ||
      aMessage == NS_MOUSE_MIDDLE_BUTTON_UP)
	{
		// remember when this happened for the next mouse down
		mLastMouseUpWhen = aOSEvent.when;
		mLastMouseUpWhere = aOSEvent.where;
	}
	else if (aMessage == NS_MOUSE_LEFT_BUTTON_DOWN  ||
           aMessage == NS_MOUSE_RIGHT_BUTTON_DOWN ||
           aMessage == NS_MOUSE_MIDDLE_BUTTON_DOWN)
	{
		// now look to see if we want to convert this to a double- or triple-click
		const short kDoubleClickMoveThreshold	= 5;
		
		if (((aOSEvent.when - mLastMouseUpWhen) < ::GetDblTime()) &&
				(((abs(aOSEvent.where.h - mLastMouseUpWhere.h) < kDoubleClickMoveThreshold) &&
				 	(abs(aOSEvent.where.v - mLastMouseUpWhere.v) < kDoubleClickMoveThreshold))))
		{		
			mClickCount ++;
			
//			if (mClickCount == 2)
//				aMessage = NS_MOUSE_LEFT_DOUBLECLICK;
		}
		else
		{
			// reset the click count, to count *this* click
			mClickCount = 1;
		}
	}

	// get the widget hit and the hit point inside that widget
	nsWindowType wtype;
	mTopLevelWidget->GetWindowType(wtype);
	PRBool          topLevelIsAPopup = (wtype == eWindowType_popup);
	WindowRef       eventTargetWindow = reinterpret_cast<WindowRef>(mTopLevelWidget->GetNativeData(NS_NATIVE_DISPLAY));

	WindowPtr       windowThatHasEvent = nsnull;
	ControlPartCode partCode = ::FindWindow(aOSEvent.where, &windowThatHasEvent);

	// if the mouse button is still down, send events to the last widget hit unless the
	// new event is in a popup window.
	nsWindow* lastWidgetHit = mEventDispatchHandler->GetWidgetHit();
	nsWindow* widgetHit = nsnull;
	if (lastWidgetHit)
	{
        // make sure we in the same window as where we started before we go assuming
        // that we know where the event will go.
        WindowRef   lastWind = reinterpret_cast<WindowRef>(lastWidgetHit->GetNativeData(NS_NATIVE_DISPLAY));
        PRBool      eventInSameWindowAsLastEvent = (windowThatHasEvent == lastWind);
        if ( eventInSameWindowAsLastEvent || !topLevelIsAPopup ) {	  
            if (::StillDown() || aMessage == NS_MOUSE_LEFT_BUTTON_UP)
            {
                widgetHit = lastWidgetHit;
                eventTargetWindow = lastWind;   // make sure we use the correct window to fix the coords
            }
            else
            {
                // Some widgets can eat mouseUp events (text widgets in TEClick, sbars in TrackControl).
                // In that case, stop considering this widget as being still hit.
                mEventDispatchHandler->SetWidgetHit(nsnull);
            }
        }
	}

	Point hitPoint = aOSEvent.where;
	nsGraphicsUtils::SafeSetPortWindowPort(eventTargetWindow);

	{
	    StOriginSetter  originSetter(eventTargetWindow);
    	::GlobalToLocal(&hitPoint);     // gives the mouse loc in local coords of the hit window
	}
	
	nsPoint widgetHitPoint(hitPoint.h, hitPoint.v);

	// if the mouse is in the grow box, pretend that it has left the window
	if ( partCode != inGrow ) {
		if (! widgetHit)
			widgetHit = mTopLevelWidget->FindWidgetHit(hitPoint);
	
		if (widgetHit)
		{
			nsRect bounds;
			widgetHit->GetBounds(bounds);
			nsPoint widgetOrigin(bounds.x, bounds.y);
			widgetHit->LocalToWindowCoordinate(widgetOrigin);
			widgetHitPoint.MoveBy(-widgetOrigin.x, -widgetOrigin.y);
		}
	}

    // if we haven't found anything and we're tracking the mouse for a popup, then
    // it's probable that the coordinates are just negative and we were dispatched
    // here just cuz we're the top window in the app right now. In that case, just
    // set the widget hit to this one. It's harmless (I hope), and it avoids asserts
    // in the view code about null widgets.
    if ( !widgetHit && topLevelIsAPopup && (hitPoint.h < 0 || hitPoint.v < 0) )
        widgetHit = mTopLevelWidget;

    InitializeMouseEvent(aMouseEvent, widgetHitPoint, aOSEvent.modifiers,
                         mClickCount);

    // nsEvent
    aMouseEvent.message     = aMessage;

    // nsGUIEvent
    aMouseEvent.widget      = widgetHit;
    aMouseEvent.nativeMsg   = (void*)&aOSEvent;

    // nsMouseEvent
    aMouseEvent.acceptActivation = PR_TRUE;
}

//-------------------------------------------------------------------------
//
// HandlePositionToOffsetEvent
//
//-------------------------------------------------------------------------
long nsMacEventHandler::HandlePositionToOffset(Point aPoint,short* regionClass)
{
	*regionClass = kTSMOutsideOfBody;
	return 0;
}

//-------------------------------------------------------------------------
//
// HandleOffsetToPosition Event
//
//-------------------------------------------------------------------------
nsresult nsMacEventHandler::HandleOffsetToPosition(long offset,Point* thePoint)
{
	thePoint->v = mIMEPos.y;
	thePoint->h = mIMEPos.x;
	//printf("local (x,y) = (%d, %d)\n", thePoint->h, thePoint->v);
	WindowRef wind = reinterpret_cast<WindowRef>(mTopLevelWidget->GetNativeData(NS_NATIVE_DISPLAY));
	nsGraphicsUtils::SafeSetPortWindowPort(wind);
	Rect savePortRect;
	::GetWindowPortBounds(wind, &savePortRect);
	::LocalToGlobal(thePoint);
	//printf("global (x,y) = (%d, %d)\n", thePoint->h, thePoint->v);

	return PR_TRUE;
}

//-------------------------------------------------------------------------
//
// HandleUpdate Event
//
//-------------------------------------------------------------------------
nsresult nsMacEventHandler::UnicodeHandleUpdateInputArea(const PRUnichar* text, long charCount,
                                                         long fixedLength, TextRangeArray* textRangeList)
{
#ifdef DEBUG_TSM
  printf("********************************************************************************\n");
  printf("nsMacEventHandler::UnicodeHandleUpdateInputArea size=%d fixlen=%d\n",charCount, fixedLength);
#endif
  nsresult res = NS_OK;
  long committedLen = 0;
  //------------------------------------------------------------------------------------------------
  // if we aren't in composition mode alredy, signal the backing store w/ the mode change
  //------------------------------------------------------------------------------------------------
  if (!mIMEIsComposing) {
    res = HandleStartComposition();
    NS_ASSERTION(NS_SUCCEEDED(res), "nsMacEventHandler::UnicodeHandleUpdateInputArea: HandleStartComposition failed.");
    if (NS_FAILED(res))
      goto error;
  }
  // mIMECompositionStr should be created in the HandleStartComposition
  NS_ASSERTION(mIMECompositionStr, "do not have mIMECompositionStr");
  if (nsnull == mIMECompositionStr)
  {
    res = NS_ERROR_OUT_OF_MEMORY;
    goto error;
  }

  //====================================================================================================
  // Note- It is possible that the UpdateInputArea event sent both committed text and uncommitted text
  // at the same time. The easiest way to do that is using Korean input method w/ "Enter by Character" option
  //====================================================================================================
  //  1. Handle the committed text
  //====================================================================================================
  committedLen = (fixedLength == -1) ? charCount : fixedLength;
  if (0 != committedLen)
  {
#ifdef DEBUG_TSM
    printf("Have commit text from 0 to %d\n", committedLen);
#endif
    //------------------------------------------------------------------------------------------------
    // 1.1 send textEvent to commit the text
    //------------------------------------------------------------------------------------------------
    mIMECompositionStr->Assign(text, committedLen);
#ifdef DEBUG_TSM
    printf("1.2====================================\n");
#endif
    res = HandleTextEvent(0, nsnull);
    NS_ASSERTION(NS_SUCCEEDED(res), "nsMacEventHandler::UnicodeHandleUpdateInputArea: HandleTextEvent failed.");
    if (NS_FAILED(res)) 
      goto error; 
    //------------------------------------------------------------------------------------------------
    // 1.3 send compositionEvent to end the composition
    //------------------------------------------------------------------------------------------------
    res = nsMacEventHandler::HandleEndComposition();
    NS_ASSERTION(NS_SUCCEEDED(res), "nsMacEventHandler::UnicodeHandleUpdateInputArea: HandleEndComposition failed.");
    if (NS_FAILED(res)) 
      goto error; 
  }    //  1. Handle the committed text

  //====================================================================================================
  //  2. Handle the uncommitted text
  //====================================================================================================
  if ((-1 != fixedLength) && (charCount != fixedLength))
  {  
#ifdef DEBUG_TSM
    printf("Have new uncommitted text from %d to text_size(%d)\n", committedLen, charCount);
#endif
    //------------------------------------------------------------------------------------------------
    // 2.1 send compositionEvent to start the composition
    //------------------------------------------------------------------------------------------------
    //
    // if we aren't in composition mode already, signal the backing store w/ the mode change
    //  
    if (!mIMEIsComposing) {
      res = HandleStartComposition();
      NS_ASSERTION(NS_SUCCEEDED(res), "nsMacEventHandler::UnicodeHandleUpdateInputArea: HandleStartComposition failed.");
      if (NS_FAILED(res))
        goto error; 
    }   // 2.1 send compositionEvent to start the composition
    //------------------------------------------------------------------------------------------------
    // 2.2 send textEvent for the uncommitted text
    //------------------------------------------------------------------------------------------------
    //------------------------------------------------------------------------------------------------
    // 2.2.1 make sure we have one range array
    //------------------------------------------------------------------------------------------------

    TextRangeArray rawTextRangeArray;
    TextRangeArray *rangeArray;
    if (textRangeList && textRangeList->fNumOfRanges) {
      rangeArray = textRangeList;
    } else {
      rangeArray = &rawTextRangeArray;
      rawTextRangeArray.fNumOfRanges = 1;
      rawTextRangeArray.fRange[0].fStart = committedLen * 2;
      rawTextRangeArray.fRange[0].fEnd = charCount * 2;
      rawTextRangeArray.fRange[0].fHiliteStyle = NS_TEXTRANGE_RAWINPUT;      
    }

    
#ifdef DEBUG_TSM
    printf("nsMacEventHandler::UnicodeHandleUpdateInputArea textRangeList is %s\n", textRangeList ? "NOT NULL" : "NULL");
#endif
    nsTextRangeArray xpTextRangeArray = new nsTextRange[rangeArray->fNumOfRanges];
    NS_ASSERTION(xpTextRangeArray!=NULL, "nsMacEventHandler::UnicodeHandleUpdateInputArea: xpTextRangeArray memory allocation failed.");
    if (xpTextRangeArray == NULL)
    {
      res = NS_ERROR_OUT_OF_MEMORY;
      goto error; 
    }
  
    //------------------------------------------------------------------------------------------------
    // 2.2.2 convert range array into our xp range array
    //------------------------------------------------------------------------------------------------
    //
    // the TEC offset mapping capabilities won't work here because you need to have unique, ordered offsets
    //  so instead we iterate over the range list and map each range individually.  it's probably faster than
    //  trying to do collapse all the ranges into a single offset list
    //
    PRInt32 i;
    for(i = 0; i < rangeArray->fNumOfRanges; i++) {      
      // 2.2.2.1 check each range item in NS_ASSERTION
      NS_ASSERTION(
        (NS_TEXTRANGE_CARETPOSITION==rangeArray->fRange[i].fHiliteStyle)||
        (NS_TEXTRANGE_RAWINPUT==rangeArray->fRange[i].fHiliteStyle)||
        (NS_TEXTRANGE_SELECTEDRAWTEXT==rangeArray->fRange[i].fHiliteStyle)||
        (NS_TEXTRANGE_CONVERTEDTEXT==rangeArray->fRange[i].fHiliteStyle)||
        (NS_TEXTRANGE_SELECTEDCONVERTEDTEXT==rangeArray->fRange[i].fHiliteStyle),
        "illegal range type");
      NS_ASSERTION( rangeArray->fRange[i].fStart/2 <= charCount, "illegal range");
      NS_ASSERTION( rangeArray->fRange[i].fEnd/2 <= charCount, "illegal range");

#ifdef DEBUG_TSM
      printf("nsMacEventHandler::UnicodeHandleUpdateInputArea textRangeList[%d] = (%d,%d) text_size = %d\n",i,
        rangeArray->fRange[i].fStart/2, rangeArray->fRange[i].fEnd/2, charCount);
#endif      
      // 2.2.2.6 put destinationOffset into xpTextRangeArray[i].mStartOffset 
      xpTextRangeArray[i].mRangeType = rangeArray->fRange[i].fHiliteStyle;
      xpTextRangeArray[i].mStartOffset = rangeArray->fRange[i].fStart / 2;
      xpTextRangeArray[i].mEndOffset   = rangeArray->fRange[i].fEnd / 2;

      // 2.2.2.7 Check the converted result in NS_ASSERTION
#ifdef DEBUG_TSM
      printf("nsMacEventHandler::UnicodeHandleUpdateInputArea textRangeList[%d] => type=%d (%d,%d)\n",i,
        xpTextRangeArray[i].mRangeType,
        xpTextRangeArray[i].mStartOffset, xpTextRangeArray[i].mEndOffset);
#endif      

      NS_ASSERTION((NS_TEXTRANGE_CARETPOSITION!=xpTextRangeArray[i].mRangeType) ||
             (xpTextRangeArray[i].mStartOffset == xpTextRangeArray[i].mEndOffset),
             "start != end in CaretPosition");
    }
    mIMECompositionStr->Assign(text+committedLen, charCount-committedLen);
    //------------------------------------------------------------------------------------------------
    // 2.2.4 send the text event
    //------------------------------------------------------------------------------------------------
#ifdef DEBUG_TSM
      printf("2.2.4====================================\n");
#endif      

    res = HandleTextEvent(rangeArray->fNumOfRanges,xpTextRangeArray);
    NS_ASSERTION(NS_SUCCEEDED(res), "nsMacEventHandler::UnicodeHandleUpdateInputArea: HandleTextEvent failed.");
    if (NS_FAILED(res)) 
      goto error; 
    delete [] xpTextRangeArray;
  } //  2. Handle the uncommitted text
  else if ((0==charCount) && (0==fixedLength))
  {
    // 3. Handle empty text event
    // This is needed when we input some uncommitted text, and then delete all of them
    // When the last delete come, we will got a text_size = 0 and fixedLength = 0
    // In that case, we need to send a text event to clean up the input hole....
    mIMECompositionStr->Truncate();      
#ifdef DEBUG_TSM
      printf("3.====================================\n");
#endif
    // 3.1 send the empty text event.
    res = HandleTextEvent(0, nsnull);
    NS_ASSERTION(NS_SUCCEEDED(res),"nsMacEventHandler::UnicodeHandleUpdateInputArea: HandleTextEvent failed.");
    if (NS_FAILED(res)) 
      goto error; 
    // 3.2 send an endComposition event, we need this to make sure the delete after this work properly.
    res = nsMacEventHandler::HandleEndComposition();
    NS_ASSERTION(NS_SUCCEEDED(res),"nsMacEventHandler::UnicodeHandleUpdateInputArea: HandleEndComposition failed.");
    if (NS_FAILED(res)) 
      goto error;     
  }
  
error:
  return res;
}

nsresult nsMacEventHandler::HandleUnicodeGetSelectedText(nsAString& outString)
{
  outString.Truncate(0);
  nsWindow* focusedWidget = mEventDispatchHandler->GetActive();
  if (!focusedWidget)
    focusedWidget = mTopLevelWidget;

  nsReconversionEvent reconversionEvent(PR_TRUE, NS_RECONVERSION_QUERY,
                                        focusedWidget);
  reconversionEvent.time = PR_IntervalNow();

  nsresult res = focusedWidget->DispatchWindowEvent(reconversionEvent);

  if (NS_SUCCEEDED(res)) {
     outString.Assign(reconversionEvent.theReply.mReconversionString);
     nsMemory::Free(reconversionEvent.theReply.mReconversionString);
  } 
  return res; 
  
}
//-------------------------------------------------------------------------
//
// HandleStartComposition
//
//-------------------------------------------------------------------------
nsresult nsMacEventHandler::HandleStartComposition(void)
{
#ifdef DEBUG_TSM
	printf("HandleStartComposition\n");
#endif
	mIMEIsComposing = PR_TRUE;
	if(nsnull == mIMECompositionStr)
		mIMECompositionStr = new nsAutoString();
	NS_ASSERTION(mIMECompositionStr, "cannot allocate mIMECompositionStr");
	if(nsnull == mIMECompositionStr)
	{
		return NS_ERROR_OUT_OF_MEMORY;
	}	
	// 
	// get the focused widget [tague: may need to rethink this later]
	//
	nsWindow* focusedWidget = mEventDispatchHandler->GetActive();
	if (!focusedWidget)
		focusedWidget = mTopLevelWidget;
	
	//
	// create the nsCompositionEvent
	//
	nsCompositionEvent compositionEvent(PR_TRUE, NS_COMPOSITION_START,
                                      focusedWidget);
	compositionEvent.time = PR_IntervalNow();

	nsresult res = focusedWidget->DispatchWindowEvent(compositionEvent);
	if(NS_SUCCEEDED(res)) {
		mIMEPos.x = compositionEvent.theReply.mCursorPosition.x;
		mIMEPos.y = compositionEvent.theReply.mCursorPosition.y
		          + compositionEvent.theReply.mCursorPosition.height;
		focusedWidget->LocalToWindowCoordinate(mIMEPos);
#ifdef DEBUG_TSM
		printf("HandleStartComposition reply (%d,%d)\n", mIMEPos.x , mIMEPos.y);
#endif
	}
	return res;
}

//-------------------------------------------------------------------------
//
// HandleEndComposition
//
//-------------------------------------------------------------------------
nsresult nsMacEventHandler::HandleEndComposition(void)
{
#ifdef DEBUG_TSM
	printf("HandleEndComposition\n");
#endif
	mIMEIsComposing = PR_FALSE;
	// 
	// get the focused widget [tague: may need to rethink this later]
	//
	nsWindow* focusedWidget = mEventDispatchHandler->GetActive();
	if (!focusedWidget)
		focusedWidget = mTopLevelWidget;
	
	//
	// create the nsCompositionEvent
	//
	nsCompositionEvent compositionEvent(PR_TRUE, NS_COMPOSITION_END,
                                      focusedWidget);
	compositionEvent.time = PR_IntervalNow();

	return(focusedWidget->DispatchWindowEvent(compositionEvent));
}

//-------------------------------------------------------------------------
//
// HandleTextEvent
//
//-------------------------------------------------------------------------
nsresult nsMacEventHandler::HandleTextEvent(PRUint32 textRangeCount, nsTextRangeArray textRangeArray)
{
#ifdef DEBUG_TSM
	printf("HandleTextEvent\n");
	PRUint32 i;
	printf("text event \n[");
	const PRUnichar *ubuf = mIMECompositionStr->get();
	for(i=0; '\0' != *ubuf; i++)
		printf("U+%04X ", *ubuf++);
	printf("] len = %d\n",i);
	if(textRangeCount > 0)
	{
		for(i=0;i<textRangeCount;i++ )
		{
			NS_ASSERTION((NS_TEXTRANGE_CARETPOSITION!=textRangeArray[i].mRangeType) ||
						 (textRangeArray[i].mStartOffset == textRangeArray[i].mEndOffset),
						 "start != end in CaretPosition");
			NS_ASSERTION(
				(NS_TEXTRANGE_CARETPOSITION==textRangeArray[i].mRangeType)||
				(NS_TEXTRANGE_RAWINPUT==textRangeArray[i].mRangeType)||
				(NS_TEXTRANGE_SELECTEDRAWTEXT==textRangeArray[i].mRangeType)||
				(NS_TEXTRANGE_CONVERTEDTEXT==textRangeArray[i].mRangeType)||
				(NS_TEXTRANGE_SELECTEDCONVERTEDTEXT==textRangeArray[i].mRangeType),
				"illegal range type");
			static char *name[6] =
			{
				"Unknown",
				"CaretPosition", 
				"RawInput", 
				"SelectedRawText",
				"ConvertedText",
				"SelectedConvertedText"
			};
			printf("[%d,%d]=%s\n", 
			textRangeArray[i].mStartOffset, 
			textRangeArray[i].mEndOffset,
			((textRangeArray[i].mRangeType<=NS_TEXTRANGE_SELECTEDCONVERTEDTEXT) ?
				name[textRangeArray[i].mRangeType] : name[0])
			);
		}
	}
#endif
	// 
	// get the focused widget [tague: may need to rethink this later]
	//
	nsWindow* focusedWidget = mEventDispatchHandler->GetActive();
	if (!focusedWidget)
		focusedWidget = mTopLevelWidget;
	
	//
	// create the nsTextEvent
	//
	nsTextEvent textEvent(PR_TRUE, NS_TEXT_TEXT, focusedWidget);
	textEvent.time = PR_IntervalNow();
	textEvent.theText = mIMECompositionStr->get();
	textEvent.rangeCount = textRangeCount;
	textEvent.rangeArray = textRangeArray;

	nsresult res = NS_OK;
	if (NS_SUCCEEDED(res = focusedWidget->DispatchWindowEvent(textEvent))) {
		mIMEPos.x = textEvent.theReply.mCursorPosition.x;
		mIMEPos.y = textEvent.theReply.mCursorPosition.y +
		            textEvent.theReply.mCursorPosition.height;
		mTopLevelWidget->LocalToWindowCoordinate(mIMEPos);
#ifdef DEBUG_TSM
		printf("HandleTextEvent reply (%d,%d)\n", mIMEPos.x , mIMEPos.y);
#endif
	} 
	return res;
}
nsresult nsMacEventHandler::ResetInputState()
{
	OSErr err = noErr;
	if (mTSMDocument) {
		// make sure we do not use input widnow even some other code turn it for default by calling 
		// ::UseInputWindow(nsnull, TRUE); 
		::UseInputWindow(mTSMDocument, FALSE); 
		err = ::FixTSMDocument(mTSMDocument);
		NS_ASSERTION( (noErr==err)||(tsmDocNotActiveErr==err)||(tsmTSNotOpenErr), "Cannot FixTSMDocument");
	}
	return NS_OK;	
}

PRBool
nsMacEventHandler::HandleKeyUpDownEvent(EventHandlerCallRef aHandlerCallRef,
                                        EventRef aEvent)
{
  ClearLastMouseUp();

  PRUint32 eventKind = ::GetEventKind(aEvent);
  NS_ASSERTION(eventKind == kEventRawKeyDown ||
               eventKind == kEventRawKeyUp,
               "Unknown event kind");

  OSStatus err = noErr;

  PRBool sendToTSM = PR_FALSE;
  if (eventKind == kEventRawKeyDown) {
    if (IsPluginFocused()) {
      if (mTSMDocument != ::TSMGetActiveDocument()) {
        // If some TSM document other than the one that we use for this window
        // is active, first try to call through to the TSM handler during a
        // keydown.  This can happen if a plugin installs its own TSM handler
        // and activates its own TSM document.
        // This is done early, before dispatching NS_KEY_DOWN because the
        // plugin will receive the keyDown event carried in the NS_KEY_DOWN.
        // If an IME session is active, this could cause the plugin to accept
        // both raw keydowns and text input from the IME session as input.
        err = ::CallNextEventHandler(aHandlerCallRef, aEvent);
        if (err == noErr) {
          // Someone other than us handled the event.  Don't send NS_KEY_DOWN.
          return PR_TRUE;
        }

        // No foreign handlers did anything.  Leave sendToTSM false so that
        // no subsequent attempts to call through to the foreign handler will
        // be made.
      }
      else {
        // The TSM document matches the one corresponding to the window.
        // An NS_KEY_DOWN event will be sent, and after that, it will be put
        // back into the handler chain to go to the TSM input handlers.
        // This assumes that the TSM handler is still ours, or that if
        // something else changed the TSM handler, that the new handler will
        // at least call through to ours.
        sendToTSM = PR_TRUE;
      }
    } else {
      // If the focus isn't currently in any plugin and we have a TSM document
      // that isn't currently active, activate it.  This helps with plugins
      // that set their own TSM document and then don't set it back, or don't
      // set it back correctly.  This should always have been the browser's
      // responsibility in any case.  This change resolves bmo bug 355071, and
      // also bmo bugs 345010 and (possibly) 318139 on the 1.8.1 branch.
      if (mTSMDocument && (mTSMDocument != ::TSMGetActiveDocument()))
        ::ActivateTSMDocument(mTSMDocument);
      sendToTSM = PR_TRUE;
    }
  }

  PRBool handled = PR_FALSE;
  nsWindow* focusedWidget = mEventDispatchHandler->GetActive();
  if (!focusedWidget)
    focusedWidget = mTopLevelWidget;

  PRUint32 modifiers = 0;
  err = ::GetEventParameter(aEvent, kEventParamKeyModifiers,
                            typeUInt32, NULL,
                            sizeof(modifiers), NULL,
                            &modifiers);
  NS_ASSERTION(err == noErr, "Could not get kEventParamKeyModifiers");

  PRUint32 keyCode = 0;
  err = ::GetEventParameter(aEvent, kEventParamKeyCode,
                            typeUInt32, NULL,
                            sizeof(keyCode), NULL,
                            &keyCode);
  NS_ASSERTION(err == noErr, "Could not get kEventParamKeyCode");

  PRUint8 charCode = 0;
  ::GetEventParameter(aEvent, kEventParamKeyMacCharCodes,
                      typeChar, NULL,
                      sizeof(charCode), NULL,
                      &charCode);
  // Failure is not a fatal condition.

  // The event's nativeMsg field historically held an EventRecord.  Some
  // consumers (plugins) rely on this behavior.  Note that
  // ConvertEventRefToEventRecord can return false and produce a null
  // event record in some cases, such as when entering an IME session.
  EventRecord eventRecord;
  ::ConvertEventRefToEventRecord(aEvent, &eventRecord);

  // kEventRawKeyDown or kEventRawKeyUp only

  PRUint32 message = (eventKind == kEventRawKeyUp ? NS_KEY_UP : NS_KEY_DOWN);
  nsKeyEvent upDownEvent(PR_TRUE, message, nsnull);
  upDownEvent.time =      PR_IntervalNow();
  upDownEvent.widget =    focusedWidget;
  upDownEvent.nativeMsg = (void*)&eventRecord;
  upDownEvent.isShift =   ((modifiers & shiftKey) != 0);
  upDownEvent.isControl = ((modifiers & controlKey) != 0);
  upDownEvent.isAlt =     ((modifiers & optionKey) != 0);
  upDownEvent.isMeta =    ((modifiers & cmdKey) != 0);
  upDownEvent.keyCode =   ConvertMacToRaptorKeyCode(charCode, keyCode,
                                                    modifiers);
  upDownEvent.charCode =  0;
  handled = focusedWidget->DispatchWindowEvent(upDownEvent);

  if (eventKind == kEventRawKeyUp)
    return handled;

  // kEventRawKeyDown only.  Prepare for a possible NS_KEY_PRESS event.

  nsWindow* checkFocusedWidget = mEventDispatchHandler->GetActive();
  if (!checkFocusedWidget)
    checkFocusedWidget = mTopLevelWidget;

  // Set a flag indicating that NS_KEY_PRESS events should not be dispatched,
  // because focus changed.
  PRBool lastIgnore = PR_FALSE;
  if (checkFocusedWidget != focusedWidget) {
    lastIgnore = mKeyIgnore;
    mKeyIgnore = PR_TRUE;
  }

  // Set a flag indicating that the NS_KEY_DOWN event came back with
  // preventDefault, and NS_KEY_PRESS events should have the same flag set.
  PRBool lastHandled = PR_FALSE;
  if (handled) {
    lastHandled = mKeyHandled;
    mKeyHandled = PR_TRUE;
  }

  if (sendToTSM) {
    // The event needs further processing.
    //  - If no input method is active, an event will be delivered to the
    //    kEventTextInputUnicodeForKeyEvent handler, which will call
    //    HandleUKeyEvent, which takes care of dispatching NS_KEY_PRESS events.
    //  - If an input method is active, an event will be delivered to the
    //    kEventTextInputUpdateActiveInputArea handler, which will call
    //    UnicodeHandleUpdateInputArea to handle the input session.
    ::CallNextEventHandler(aHandlerCallRef, aEvent);
  }

  mKeyHandled = lastHandled;
  mKeyIgnore = lastIgnore;

  return handled;
}

PRBool
nsMacEventHandler::HandleKeyModifierEvent(EventHandlerCallRef aHandlerCallRef,
                                          EventRef aEvent)
{
  ClearLastMouseUp();

  PRBool handled = PR_FALSE;
  nsWindow* focusedWidget = mEventDispatchHandler->GetActive();
  if (!focusedWidget)
    focusedWidget = mTopLevelWidget;

  PRUint32 modifiers = 0;
  OSStatus err = ::GetEventParameter(aEvent, kEventParamKeyModifiers,
                                     typeUInt32, NULL,
                                     sizeof(modifiers), NULL,
                                     &modifiers);
  NS_ASSERTION(err == noErr, "Could not get kEventParamKeyModifiers");

  typedef struct {
    PRUint32 modifierBit;
    PRUint32 keycode;
  } ModifierToKeycode;
  const ModifierToKeycode kModifierToKeycodeTable[] = {
    { shiftKey,   NS_VK_SHIFT },
    { controlKey, NS_VK_CONTROL },
    { optionKey,  NS_VK_ALT },
    { cmdKey,     NS_VK_META },
  };
  const PRUint32 kModifierCount = sizeof(kModifierToKeycodeTable) /
                                  sizeof(ModifierToKeycode);

  // This will be a null event, but include it anyway because it'll still have
  // good when, where, and modifiers fields, and because it's present in other
  // NS_KEY_DOWN and NS_KEY_UP events.
  EventRecord eventRecord;
  ::ConvertEventRefToEventRecord(aEvent, &eventRecord);

  for(PRUint32 i = 0 ; i < kModifierCount ; i++) {
    PRUint32 modifierBit = kModifierToKeycodeTable[i].modifierBit;
    if ((modifiers & modifierBit) != (mLastModifierState & modifierBit)) {
      PRUint32 message = ((modifiers & modifierBit) != 0 ? NS_KEY_DOWN :
                                                           NS_KEY_UP);
      nsKeyEvent upDownEvent(PR_TRUE, message, nsnull);
      upDownEvent.time =      PR_IntervalNow();
      upDownEvent.widget =    focusedWidget;
      upDownEvent.nativeMsg = (void*)&eventRecord;
      upDownEvent.isShift =   ((modifiers & shiftKey) != 0);
      upDownEvent.isControl = ((modifiers & controlKey) != 0);
      upDownEvent.isAlt =     ((modifiers & optionKey) != 0);
      upDownEvent.isMeta =    ((modifiers & cmdKey) != 0);
      upDownEvent.keyCode =   kModifierToKeycodeTable[i].keycode;
      upDownEvent.charCode =  0;
      handled |= focusedWidget->DispatchWindowEvent(upDownEvent);

      // No need to preventDefault additional events.  Although
      // kEventRawKeyModifiersChanged can carry multiple modifiers going
      // up or down, Gecko treats them independently.
 
      // Stop if focus has changed.
      nsWindow* checkFocusedWidget = mEventDispatchHandler->GetActive();
      if (!checkFocusedWidget)
        checkFocusedWidget = mTopLevelWidget;

      if (checkFocusedWidget != focusedWidget)
        break;
    }
  }

  mLastModifierState = modifiers;
  return handled;
}

void
nsMacEventHandler::ClearLastMouseUp()
{
  mLastMouseUpWhere.h = 0;
  mLastMouseUpWhere.v = 0;
  mLastMouseUpWhen = 0;
  mClickCount = 0;
}

// Returns NS_TRUE if a plugin is currently focused, otherwise returns
// NS_FALSE.
PRBool
nsMacEventHandler::IsPluginFocused()
{
  PRBool retval = PR_FALSE;
  nsIWidget* widget = mEventDispatchHandler->GetActive();
  nsIDocument *doc = nsToolkit::GetDocumentFor(widget);
  if (doc) {
    nsIPresShell* presShell = doc->GetShellAt(0);
    nsIEventStateManager* esm = presShell->GetPresContext()->EventStateManager();
    nsIFrame *focusedFrame = nsnull;
    esm->GetFocusedFrame(&focusedFrame);
    if (focusedFrame) {
      nsIObjectFrame *pluginFrame;
      // There's no need to call Release() on objectIFrame, since AddRef()
      // and Release() don't work on frames.
      if (NS_SUCCEEDED(CallQueryInterface(focusedFrame, &pluginFrame)))
        retval = PR_TRUE;
    }
  }
  return retval;
}

void
nsMacEventHandler::ClearWindowRefs(nsWindow* aWindow)
{
  if (sLastActive && sLastActive->mTopLevelWidget == aWindow) {
    sLastActive = nsnull;
  }
}

