/* -*- Mode: C++; tab-width: 2; 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):
 *   Paul Ashford <arougthopher@lizardland.net>
 *   Sergei Dolgov <sergei_d@fi.tartu.ee>
 *   Fredrik Holmqvist <thesuckiestemail@yahoo.se>
 *   Mats Palmgren <mats.palmgren@bredband.net>
 *
 * 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 "nsDebug.h"
#include "nsWindow.h"
#include "nsIAppShell.h"
#include "nsIFontMetrics.h"
#include "nsFont.h"
#include "nsGUIEvent.h"
#include "nsWidgetsCID.h"
#include "nsIDragService.h"
#include "nsIDragSessionBeOS.h"
#include "nsIDeviceContext.h"
#include "nsRect.h"
#include "nsIRegion.h"
#include "nsTransform2D.h"
#include "nsGfxCIID.h"
#include "resource.h"
#include "prtime.h"
#include "nsReadableUtils.h"
#include "nsVoidArray.h"
#include "nsIProxyObjectManager.h"

#include <Application.h>
#include <InterfaceDefs.h>
#include <Region.h>
#include <ScrollBar.h>
#include <app/Message.h>
#include <support/String.h>
#include <Screen.h>

#include <nsBeOSCursors.h>
#if defined(BeIME)
#include <Input.h>
#include <InputServerMethod.h>
#include <String.h>
#endif
#include "nsIRollupListener.h"
#include "nsIMenuRollup.h"

#include "gfxBeOSSurface.h"
#include "gfxContext.h"

// See comments in nsWindow.h as to why we override these calls from nsBaseWidget
NS_IMPL_THREADSAFE_ADDREF(nsWindow)
NS_IMPL_THREADSAFE_RELEASE(nsWindow)

static NS_DEFINE_IID(kIWidgetIID,       NS_IWIDGET_IID);
static NS_DEFINE_IID(kRegionCID, NS_REGION_CID);
static NS_DEFINE_IID(kCDragServiceCID,  NS_DRAGSERVICE_CID);
//-------------------------------------------------------------------------
// Global Definitions
//-------------------------------------------------------------------------

// Rollup Listener - static variable defintions
static nsIRollupListener * gRollupListener           = nsnull;
static nsIWidget         * gRollupWidget             = nsnull;
static PRBool              gRollupConsumeRollupEvent = PR_FALSE;
// Tracking last activated BWindow
static BWindow           * gLastActiveWindow = NULL;

// BCursor objects can't be created until they are used.  Some mozilla utilities, 
// such as regxpcom, do not create a BApplication object, and therefor fail to run.,
// since a BCursor requires a vaild BApplication (see Bug#129964).  But, we still want
// to cache them for performance.  Currently, there are 17 cursors available;
static nsVoidArray		gCursorArray(21);
// Used in contrain position.  Specifies how much of a window must remain on screen
#define kWindowPositionSlop 20
// BeOS does not provide this information, so we must hard-code it
#define kWindowBorderWidth 5
#define kWindowTitleBarHeight 24

// TODO: make a #def for using OutLine view or not (see TODO below)
#if defined(BeIME)
#include "nsUTF8Utils.h"
static inline uint32 utf8_str_len(const char* ustring, int32 length) 
{
	CalculateUTF8Length cutf8;
	cutf8.write(ustring, length);
	return cutf8.Length();       
}

nsIMEBeOS::nsIMEBeOS()
	: imeTarget(NULL)
	, imeState(NS_COMPOSITION_END), imeWidth(14)
{
}
/* placeholder for possible cleanup
nsIMEBeOS::~nsIMEBeOS()
{
}
*/								
void nsIMEBeOS::RunIME(uint32 *args, nsWindow *target, BView *fView)
{
	BMessage msg;
	msg.Unflatten((const char*)args);

	switch (msg.FindInt32("be:opcode")) 
	{
	case B_INPUT_METHOD_CHANGED:
		if (msg.HasString("be:string")) 
		{
			const char* src = msg.FindString("be:string");
			CopyUTF8toUTF16(src, imeText);
 
    		if (msg.FindBool("be:confirmed")) 
    		{	
    			if (imeState != NS_COMPOSITION_END)
   					DispatchText(imeText, 0, NULL);
   			}
   			else 
   			{
   				nsTextRange txtRuns[2];
   				PRUint32 txtCount = 2;

	 	    	int32 select[2];
 				select[0] = msg.FindInt32("be:selection", int32(0));
				select[1] = msg.FindInt32("be:selection", 1);

	 			txtRuns[0].mStartOffset = (select[0] == select[1]) ? 0 : utf8_str_len(src, select[1]);
	 			txtRuns[0].mEndOffset	= imeText.Length();
				txtRuns[0].mRangeType	= NS_TEXTRANGE_CONVERTEDTEXT;
				if (select[0] == select[1])
					txtCount = 1;
				else 
				{
	 				txtRuns[1].mStartOffset = utf8_str_len(src, select[0]);
	 				txtRuns[1].mEndOffset	= utf8_str_len(src, select[1]);
	 				txtRuns[1].mRangeType	= NS_TEXTRANGE_SELECTEDCONVERTEDTEXT;
	 			}
	 			imeTarget = target;
				DispatchText(imeText, txtCount, txtRuns);
			}	
		}	
		break;

	case B_INPUT_METHOD_LOCATION_REQUEST:
		if (fView && fView->LockLooper()) 
		{
			BPoint caret(imeCaret);
			DispatchIME(NS_COMPOSITION_QUERY);
			if (caret.x > imeCaret.x) 
				caret.x = imeCaret.x - imeWidth * imeText.Length();	/* back */

			BMessage reply(B_INPUT_METHOD_EVENT);
			reply.AddInt32("be:opcode", B_INPUT_METHOD_LOCATION_REQUEST);
			for (int32 s= 0; imeText[s]; s++) 
			{ 
				reply.AddPoint("be:location_reply", fView->ConvertToScreen(caret));
				reply.AddFloat("be:height_reply", imeHeight);
				caret.x += imeWidth;
			}
			imeMessenger.SendMessage(&reply);
			fView->UnlockLooper();
		}
		break;

	case B_INPUT_METHOD_STARTED:
		imeTarget = target;
		DispatchIME(NS_COMPOSITION_START);
		DispatchIME(NS_COMPOSITION_QUERY);

		msg.FindMessenger("be:reply_to", &imeMessenger);
		break;
	
	case B_INPUT_METHOD_STOPPED:
		if (imeState != NS_COMPOSITION_END)
			DispatchIME(NS_COMPOSITION_END);
		imeText.Truncate();
		break;
	};
}

void nsIMEBeOS::DispatchText(nsString &text, PRUint32 txtCount, nsTextRange* txtRuns)
{
	nsTextEvent textEvent(PR_TRUE,NS_TEXT_TEXT, imeTarget);

	textEvent.time 		= 0;
	textEvent.isShift   = 
	textEvent.isControl =
	textEvent.isAlt 	= 
	textEvent.isMeta 	= PR_FALSE;
  
	textEvent.refPoint.x	= 
	textEvent.refPoint.y	= 0;

	textEvent.theText 	= text.get();
	textEvent.isChar	= PR_TRUE;
	textEvent.rangeCount= txtCount;
	textEvent.rangeArray= txtRuns;

	DispatchWindowEvent(&textEvent);
}

void nsIMEBeOS::DispatchCancelIME()
{
	if (imeText.Length() && imeState != NS_COMPOSITION_END) 
	{
		BMessage reply(B_INPUT_METHOD_EVENT);
		reply.AddInt32("be:opcode", B_INPUT_METHOD_STOPPED);
		imeMessenger.SendMessage(&reply);

		DispatchText(imeText, 0, NULL);
		DispatchIME(NS_COMPOSITION_END);

		imeText.Truncate();
	}
}

void nsIMEBeOS::DispatchIME(PRUint32 what)
{
	nsCompositionEvent compEvent(PR_TRUE, what, imeTarget);

	compEvent.refPoint.x =
	compEvent.refPoint.y = 0;
	compEvent.time 	 = 0;

	DispatchWindowEvent(&compEvent);
	imeState = what;

	if (what == NS_COMPOSITION_QUERY) 
	{
		imeCaret.Set(compEvent.theReply.mCursorPosition.x,
		           compEvent.theReply.mCursorPosition.y);
		imeHeight = compEvent.theReply.mCursorPosition.height+4;
	}
}

PRBool nsIMEBeOS::DispatchWindowEvent(nsGUIEvent* event)
{
	nsEventStatus status;
	imeTarget->DispatchEvent(event, status);
	return PR_FALSE;
}
// There is only one IME instance per app, actually it may be set as global
nsIMEBeOS *nsIMEBeOS::GetIME()
{
	if(beosIME == 0)
		beosIME = new nsIMEBeOS();
	return beosIME;
}
nsIMEBeOS *nsIMEBeOS::beosIME = 0;
#endif
//-------------------------------------------------------------------------
//
// nsWindow constructor
//
//-------------------------------------------------------------------------
nsWindow::nsWindow() : nsBaseWidget()
{
	mView               = 0;
	mPreferredWidth     = 0;
	mPreferredHeight    = 0;
	mFontMetrics        = nsnull;
	mIsShiftDown        = PR_FALSE;
	mIsControlDown      = PR_FALSE;
	mIsAltDown          = PR_FALSE;
	mIsDestroying       = PR_FALSE;
	mIsVisible          = PR_FALSE;
	mEnabled            = PR_TRUE;
	mIsScrolling        = PR_FALSE;
	mParent             = nsnull;
	mWindowParent       = nsnull;
	mUpdateArea = do_CreateInstance(kRegionCID);
	mForeground = NS_RGBA(0xFF,0xFF,0xFF,0xFF);
	mBackground = mForeground;
	mBWindowFeel        = B_NORMAL_WINDOW_FEEL;
	mBWindowLook        = B_NO_BORDER_WINDOW_LOOK;

	if (mUpdateArea)
	{
		mUpdateArea->Init();
		mUpdateArea->SetTo(0, 0, 0, 0);
	}
}


//-------------------------------------------------------------------------
//
// nsWindow destructor
//
//-------------------------------------------------------------------------
nsWindow::~nsWindow()
{
	mIsDestroying = PR_TRUE;

	// If the widget was released without calling Destroy() then the native
	// window still exists, and we need to destroy it
	if (NULL != mView) 
	{
		Destroy();
	}
	NS_IF_RELEASE(mFontMetrics);
}

NS_METHOD nsWindow::BeginResizingChildren(void)
{
	// HideKids(PR_TRUE) may be used here
	NS_NOTYETIMPLEMENTED("BeginResizingChildren not yet implemented"); // to be implemented
	return NS_OK;
}

NS_METHOD nsWindow::EndResizingChildren(void)
{
	// HideKids(PR_FALSE) may be used here
	NS_NOTYETIMPLEMENTED("EndResizingChildren not yet implemented"); // to be implemented
	return NS_OK;
}

NS_METHOD nsWindow::WidgetToScreen(const nsRect& aOldRect, nsRect& aNewRect)
{
	BPoint	point;
	point.x = aOldRect.x;
	point.y = aOldRect.y;
	if (mView && mView->LockLooper())
	{
		mView->ConvertToScreen(&point);
		mView->UnlockLooper();
	}
	aNewRect.x = nscoord(point.x);
	aNewRect.y = nscoord(point.y);
	aNewRect.width = aOldRect.width;
	aNewRect.height = aOldRect.height;
	return NS_OK;
}

NS_METHOD nsWindow::ScreenToWidget(const nsRect& aOldRect, nsRect& aNewRect)
{
	BPoint	point;
	point.x = aOldRect.x;
	point.y = aOldRect.y;
	if (mView && mView->LockLooper())
	{
		mView->ConvertFromScreen(&point);
		mView->UnlockLooper();
	}
	aNewRect.x = nscoord(point.x);
	aNewRect.y = nscoord(point.y);
	aNewRect.width = aOldRect.width;
	aNewRect.height = aOldRect.height;
	return NS_OK;
}


//-------------------------------------------------------------------------
//
// Initialize an event to dispatch
//
//-------------------------------------------------------------------------
void nsWindow::InitEvent(nsGUIEvent& event, nsPoint* aPoint)
{
	NS_ADDREF(event.widget);

	if (nsnull == aPoint) // use the point from the event
	{
		// get the message position in client coordinates and in twips
		event.refPoint.x = 0;
		event.refPoint.y = 0;
	}
	else // use the point override if provided
	{
		event.refPoint.x = aPoint->x;
		event.refPoint.y = aPoint->y;
	}
	event.time = PR_IntervalNow();
}

//-------------------------------------------------------------------------
//
// Invokes callback and  ProcessEvent method on Event Listener object
//
//-------------------------------------------------------------------------
NS_IMETHODIMP nsWindow::DispatchEvent(nsGUIEvent* event, nsEventStatus & aStatus)
{
	aStatus = nsEventStatus_eIgnore;

	nsCOMPtr <nsIWidget> mWidget = event->widget;

	if (mEventCallback)
		aStatus = (*mEventCallback)(event);

	if ((aStatus != nsEventStatus_eIgnore) && (mEventListener))
		aStatus = mEventListener->ProcessEvent(*event);

	return NS_OK;
}

//-------------------------------------------------------------------------
//
// Dispatch Window Event
//
//-------------------------------------------------------------------------
PRBool nsWindow::DispatchWindowEvent(nsGUIEvent* event)
{
	nsEventStatus status;
	DispatchEvent(event, status);
	return ConvertStatus(status);
}

//-------------------------------------------------------------------------
//
// Dispatch standard event
//
//-------------------------------------------------------------------------

PRBool nsWindow::DispatchStandardEvent(PRUint32 aMsg)
{
	nsGUIEvent event(PR_TRUE, aMsg, this);
	InitEvent(event);

	PRBool result = DispatchWindowEvent(&event);
	NS_RELEASE(event.widget);
	return result;
}

NS_IMETHODIMP nsWindow::PreCreateWidget(nsWidgetInitData *aInitData)
{
	if ( nsnull == aInitData)
		return NS_ERROR_FAILURE;
	
	SetWindowType(aInitData->mWindowType);
	SetBorderStyle(aInitData->mBorderStyle);
	return NS_OK;
}

//-------------------------------------------------------------------------
//
// Utility method for implementing both Create(nsIWidget ...) and
// Create(nsNativeWidget...)
//-------------------------------------------------------------------------
nsresult nsWindow::StandardWindowCreate(nsIWidget *aParent,
                                        const nsRect &aRect,
                                        EVENT_CALLBACK aHandleEventFunction,
                                        nsIDeviceContext *aContext,
                                        nsIAppShell *aAppShell,
                                        nsIToolkit *aToolkit,
                                        nsWidgetInitData *aInitData,
                                        nsNativeWidget aNativeParent)
{

	//Do as little as possible for invisible windows, why are these needed?
	if (aInitData->mWindowType == eWindowType_invisible)
		return NS_ERROR_FAILURE;
		
	NS_ASSERTION(aInitData->mWindowType == eWindowType_dialog
		|| aInitData->mWindowType == eWindowType_toplevel,
		"The windowtype is not handled by this class.");

	mIsTopWidgetWindow = PR_TRUE;
	
	BaseCreate(nsnull, aRect, aHandleEventFunction, aContext,
	           aAppShell, aToolkit, aInitData);

	mListenForResizes = aNativeParent ? PR_TRUE : aInitData->mListenForResizes;
		
	mParent = aParent;
	// Useful shortcut, wondering if we can use it also in GetParent() instead
	// nsIWidget* type mParent.
	mWindowParent = (nsWindow *)aParent;
	SetBounds(aRect);

	// Default mode for window, everything switched off.
	uint32 flags = B_NOT_RESIZABLE | B_NOT_MINIMIZABLE | B_NOT_ZOOMABLE
		| B_NOT_CLOSABLE | B_ASYNCHRONOUS_CONTROLS;

	//eBorderStyle_default is to ask the OS to handle it as it sees best.
	//eBorderStyle_all is same as top_level window default.
	if (eBorderStyle_default == mBorderStyle || eBorderStyle_all & mBorderStyle)
	{
		//(Firefox prefs doesn't go this way, so apparently it wants titlebar, zoom, 
		//resize and close.)

		//Look and feel for others are set ok at init.
		if (eWindowType_toplevel==mWindowType)
		{
			mBWindowLook = B_TITLED_WINDOW_LOOK;
			flags = B_ASYNCHRONOUS_CONTROLS;
		}
	}
	else
	{
		if (eBorderStyle_border & mBorderStyle)
			mBWindowLook = B_MODAL_WINDOW_LOOK;

		if (eBorderStyle_resizeh & mBorderStyle)
		{
			//Resize demands at least border
			mBWindowLook = B_MODAL_WINDOW_LOOK;
			flags &= !B_NOT_RESIZABLE;
		}

		//We don't have titlebar menus, so treat like title as it demands titlebar.
		if (eBorderStyle_title & mBorderStyle || eBorderStyle_menu & mBorderStyle)
			mBWindowLook = B_TITLED_WINDOW_LOOK;

		if (eBorderStyle_minimize & mBorderStyle)
			flags &= !B_NOT_MINIMIZABLE;

		if (eBorderStyle_maximize & mBorderStyle)
			flags &= !B_NOT_ZOOMABLE;

		if (eBorderStyle_close & mBorderStyle)
			flags &= !B_NOT_CLOSABLE;
	}

	nsWindowBeOS * w = new nsWindowBeOS(this, 
		BRect(aRect.x, aRect.y, aRect.x + aRect.width - 1, aRect.y + aRect.height - 1),
		"", mBWindowLook, mBWindowFeel, flags);
	if (!w)
		return NS_ERROR_OUT_OF_MEMORY;

	mView = new nsViewBeOS(this, w->Bounds(), "Toplevel view", B_FOLLOW_ALL, 0);

	if (!mView)
		return NS_ERROR_OUT_OF_MEMORY;

	w->AddChild(mView);
	// I'm wondering if we can move part of that code to above
	if (eWindowType_dialog == mWindowType && mWindowParent) 
	{
		nsWindow *topparent = mWindowParent;
		while (topparent->mWindowParent)
			topparent = topparent->mWindowParent;
		// may be got via mView and mView->Window() of topparent explicitly	
		BWindow* subsetparent = (BWindow *)
			topparent->GetNativeData(NS_NATIVE_WINDOW);
		if (subsetparent)
		{
			mBWindowFeel = B_FLOATING_SUBSET_WINDOW_FEEL;
			w->SetFeel(mBWindowFeel);
			w->AddToSubset(subsetparent);
		}
	} 
	// Run Looper. No proper destroy without it.
	w->Run();
	DispatchStandardEvent(NS_CREATE);
	return NS_OK;
}

//-------------------------------------------------------------------------
//
// Create the proper widget
//
//-------------------------------------------------------------------------
NS_METHOD nsWindow::Create(nsIWidget *aParent,
                           const nsRect &aRect,
                           EVENT_CALLBACK aHandleEventFunction,
                           nsIDeviceContext *aContext,
                           nsIAppShell *aAppShell,
                           nsIToolkit *aToolkit,
                           nsWidgetInitData *aInitData)
{
	// Switch to the "main gui thread" if necessary... This method must
	// be executed on the "gui thread"...

	nsToolkit* toolkit = (nsToolkit *)mToolkit;
	if (toolkit && !toolkit->IsGuiThread())
	{
		nsCOMPtr<nsIWidget> widgetProxy;
		nsresult rv = NS_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
										NS_GET_IID(nsIWidget),
										this, 
										NS_PROXY_SYNC | NS_PROXY_ALWAYS, 
										getter_AddRefs(widgetProxy));
	
		if (NS_FAILED(rv))
			return rv;
		return widgetProxy->Create(aParent, aRect, aHandleEventFunction, aContext,
                           			aAppShell, aToolkit, aInitData);
	}
	return(StandardWindowCreate(aParent, aRect, aHandleEventFunction,
	                            aContext, aAppShell, aToolkit, aInitData,
	                            nsnull));
}


//-------------------------------------------------------------------------
//
// create with a native parent
//
//-------------------------------------------------------------------------

NS_METHOD nsWindow::Create(nsNativeWidget aParent,
                           const nsRect &aRect,
                           EVENT_CALLBACK aHandleEventFunction,
                           nsIDeviceContext *aContext,
                           nsIAppShell *aAppShell,
                           nsIToolkit *aToolkit,
                           nsWidgetInitData *aInitData)
{
	// Switch to the "main gui thread" if necessary... This method must
	// be executed on the "gui thread"...

	nsToolkit* toolkit = (nsToolkit *)mToolkit;
	if (toolkit && !toolkit->IsGuiThread())
	{
		nsCOMPtr<nsIWidget> widgetProxy;
		nsresult rv = NS_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
										NS_GET_IID(nsIWidget),
										this, 
										NS_PROXY_SYNC | NS_PROXY_ALWAYS, 
										getter_AddRefs(widgetProxy));
	
		if (NS_FAILED(rv))
			return rv;
		return widgetProxy->Create(aParent, aRect, aHandleEventFunction, aContext,
                           			aAppShell, aToolkit, aInitData);
	}
	return(StandardWindowCreate(nsnull, aRect, aHandleEventFunction,
	                            aContext, aAppShell, aToolkit, aInitData,
	                            aParent));
}

gfxASurface*
nsWindow::GetThebesSurface()
{
	mThebesSurface = nsnull;
	if (!mThebesSurface) {
		mThebesSurface = new gfxBeOSSurface(mView);
	}
	return mThebesSurface;
}

//-------------------------------------------------------------------------
//
// Close this nsWindow
//
//-------------------------------------------------------------------------
NS_METHOD nsWindow::Destroy()
{
	// Switch to the "main gui thread" if necessary... This method must
	// be executed on the "gui thread"...
	nsToolkit* toolkit = (nsToolkit *)mToolkit;
	if (toolkit != nsnull && !toolkit->IsGuiThread())
	{
		nsCOMPtr<nsIWidget> widgetProxy;
		nsresult rv = NS_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
										NS_GET_IID(nsIWidget),
										this, 
										NS_PROXY_SYNC | NS_PROXY_ALWAYS, 
										getter_AddRefs(widgetProxy));
	
		if (NS_FAILED(rv))
			return rv;
		return widgetProxy->Destroy();
	}
	// Ok, now tell the nsBaseWidget class to clean up what it needs to
	if (!mIsDestroying)
	{
		nsBaseWidget::Destroy();
	}	
	//our windows can be subclassed by
	//others and these namless, faceless others
	//may not let us know about WM_DESTROY. so,
	//if OnDestroy() didn't get called, just call
	//it now.
	if (PR_FALSE == mOnDestroyCalled)
		OnDestroy();
	
	// Destroy the BView, if no mView, it is probably destroyed before
	// automatically with BWindow::Quit()
	if (mView)
	{
		// prevent the widget from causing additional events
		mEventCallback = nsnull;
	
		if (mView->LockLooper())
		{
			while(mView->ChildAt(0))
				mView->RemoveChild(mView->ChildAt(0));
			// destroy from inside
			BWindow	*w = mView->Window();
			// if no window, it was destroyed as result of B_QUIT_REQUESTED and 
			// took also all its children away
			if (w)
			{
				w->Sync();
				if (mView->Parent())
				{
					mView->Parent()->RemoveChild(mView);
					if (eWindowType_child != mWindowType)
						w->Quit();
					else
					w->Unlock();
				}
				else
				{
					w->RemoveChild(mView);
					w->Quit();
				}
			}
			else
				mView->RemoveSelf();

			delete mView;
		}

		// window is already gone
		mView = NULL;
	}
	mParent = nsnull;
	mWindowParent = nsnull;
	return NS_OK;
}


//-------------------------------------------------------------------------
//
// Get this nsWindow parent
//
//-------------------------------------------------------------------------
nsIWidget* nsWindow::GetParent(void)
{
	//We cannot addref mParent directly
	nsIWidget	*widget = 0;
	if (mIsDestroying || mOnDestroyCalled)
		return nsnull;
	widget = (nsIWidget *)mParent;
	return  widget;
}


//-------------------------------------------------------------------------
//
// Hide or show this component
//
//-------------------------------------------------------------------------
NS_METHOD nsWindow::Show(PRBool bState)
{
	if (!mEnabled)
		return NS_OK;
		

	if (!mView || !mView->LockLooper())
		return NS_OK;
		
	//We need to do the IsHidden() checks
	//because BeOS counts no of Hide()
	//and Show() checks. BeBook:
	// If Hide() is called more than once, you'll need to call Show()
	// an equal number of times for the window to become visible again.
	if (bState == PR_FALSE)
	{
		if (mView->Window() && !mView->Window()->IsHidden())
			mView->Window()->Hide();
	}
	else
	{
		if (mView->Window() && mView->Window()->IsHidden())
			mView->Window()->Show();
	}

	mView->UnlockLooper();
	mIsVisible = bState;	
	
	return NS_OK;
}
//-------------------------------------------------------------------------
// Set/unset mouse capture
//-------------------------------------------------------------------------
NS_METHOD nsWindow::CaptureMouse(PRBool aCapture)
{
	if (mView && mView->LockLooper())
	{
		if (PR_TRUE == aCapture)
			mView->SetEventMask(B_POINTER_EVENTS);
		else
			mView->SetEventMask(0);
		mView->UnlockLooper();
	}
	return NS_OK;
}
//-------------------------------------------------------------------------
// Capture Roolup Events
//-------------------------------------------------------------------------
NS_METHOD nsWindow::CaptureRollupEvents(nsIRollupListener * aListener, PRBool aDoCapture, PRBool aConsumeRollupEvent)
{
	if (!mEnabled)
		return NS_OK;
		
	if (aDoCapture) 
	{
		// we haven't bothered carrying a weak reference to gRollupWidget because
		// we believe lifespan is properly scoped. this next assertion helps 
		// assure that remains true.
		NS_ASSERTION(!gRollupWidget, "rollup widget reassigned before release");
		gRollupConsumeRollupEvent = aConsumeRollupEvent;
		NS_IF_RELEASE(gRollupListener);
		NS_IF_RELEASE(gRollupWidget);
		gRollupListener = aListener;
		NS_ADDREF(aListener);
		gRollupWidget = this;
		NS_ADDREF(this);
	} 
	else 
	{
		NS_IF_RELEASE(gRollupListener);
		NS_IF_RELEASE(gRollupWidget);
	}

	return NS_OK;
}

//-------------------------------------------------------------------------
// Check if event happened inside the given nsWindow
//-------------------------------------------------------------------------
PRBool nsWindow::EventIsInsideWindow(nsWindow* aWindow, nsPoint pos)
{
	BRect r;
	BWindow *window = (BWindow *)aWindow->GetNativeData(NS_NATIVE_WINDOW);
	if (window)
	{
		r = window->Frame();
	}
	else
	{
		// Bummer!
		return PR_FALSE;
	}

	if (pos.x < r.left || pos.x > r.right ||
	    pos.y < r.top || pos.y > r.bottom)
	{
		return PR_FALSE;
	}

	return PR_TRUE;
}

//-------------------------------------------------------------------------
// DealWithPopups
//
// Handle events that may cause a popup (combobox, XPMenu, etc) to need to rollup.
//-------------------------------------------------------------------------
PRBool
nsWindow::DealWithPopups(uint32 methodID, nsPoint pos)
{
	if (gRollupListener && gRollupWidget) 
	{
		// Rollup if the event is outside the popup.
		PRBool rollup = !nsWindow::EventIsInsideWindow((nsWindow*)gRollupWidget, pos);

		// 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.
		if (rollup) 
		{
			nsCOMPtr<nsIMenuRollup> menuRollup ( do_QueryInterface(gRollupListener) );
			if ( menuRollup ) 
			{
				nsAutoTArray<nsIWidget*, 5> widgetChain;
				menuRollup->GetSubmenuWidgetChain(&widgetChain);

				for ( PRUint32 i = 0; i < widgetChain.Length(); ++i ) 
				{
					nsIWidget* widget = widgetChain[i];
					if ( nsWindow::EventIsInsideWindow((nsWindow*)widget, pos) ) 
					{
						rollup = PR_FALSE;
						break;
					}
				} // foreach parent menu widget
			} // if rollup listener knows about menus
		} // if rollup

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

			if (gRollupConsumeRollupEvent) 
			{
				return PR_TRUE;
			}
		}
	} // if rollup listeners registered

	return PR_FALSE;
}


//-------------------------------------------------------------------------
//
// IsVisible
//
// Return PR_TRUE if the whether the component is visible, PR_FALSE otherwise
//-------------------------------------------------------------------------
NS_METHOD nsWindow::IsVisible(PRBool & bState)
{
	bState = mIsVisible && mView && mView->Visible();
	return NS_OK;
}

//-------------------------------------------------------------------------
//
// Hide window borders/decorations for this widget
//
//-------------------------------------------------------------------------
NS_METHOD nsWindow::HideWindowChrome(PRBool aShouldHide)
{
	if(mWindowType == eWindowType_child || mView == 0 || mView->Window() == 0)
		return NS_ERROR_FAILURE;
	// B_BORDERED 
	if (aShouldHide)
		mView->Window()->SetLook(B_NO_BORDER_WINDOW_LOOK);
	else
		mView->Window()->SetLook(mBWindowLook);
	return NS_OK;
}

//-------------------------------------------------------------------------
//
// Sanity check potential move coordinates
//
//-------------------------------------------------------------------------
NS_METHOD nsWindow::ConstrainPosition(PRBool aAllowSlop, PRInt32 *aX, PRInt32 *aY)
{
	if (mIsTopWidgetWindow && mView->Window()) 
	{
		BScreen screen;
		// If no valid screen, just return
		if (! screen.IsValid()) return NS_OK;
		
		BRect screen_rect = screen.Frame();
		BRect win_bounds = mView->Window()->Frame();

#ifdef DEBUG_CONSTRAIN_POSITION
		printf("ConstrainPosition: allowSlop=%s, x=%d, y=%d\n\tScreen :", (aAllowSlop?"T":"F"),*aX,*aY);
		screen_rect.PrintToStream();
		printf("\tWindow: ");
		win_bounds.PrintToStream();
#endif
		
		if (aAllowSlop) 
		{
			if (*aX < kWindowPositionSlop - win_bounds.IntegerWidth() + kWindowBorderWidth)
				*aX = kWindowPositionSlop - win_bounds.IntegerWidth() + kWindowBorderWidth;
			else if (*aX > screen_rect.IntegerWidth() - kWindowPositionSlop - kWindowBorderWidth)
				*aX = screen_rect.IntegerWidth() - kWindowPositionSlop - kWindowBorderWidth;
				
			if (*aY < kWindowPositionSlop - win_bounds.IntegerHeight() + kWindowTitleBarHeight)
				*aY = kWindowPositionSlop - win_bounds.IntegerHeight() + kWindowTitleBarHeight;
			else if (*aY > screen_rect.IntegerHeight() - kWindowPositionSlop - kWindowBorderWidth)
				*aY = screen_rect.IntegerHeight() - kWindowPositionSlop - kWindowBorderWidth;
				
		} 
		else 
		{
			
			if (*aX < kWindowBorderWidth)
				*aX = kWindowBorderWidth;
			else if (*aX > screen_rect.IntegerWidth() - win_bounds.IntegerWidth() - kWindowBorderWidth)
				*aX = screen_rect.IntegerWidth() - win_bounds.IntegerWidth() - kWindowBorderWidth;
				
			if (*aY < kWindowTitleBarHeight)
				*aY = kWindowTitleBarHeight;
			else if (*aY > screen_rect.IntegerHeight() - win_bounds.IntegerHeight() - kWindowBorderWidth)
				*aY = screen_rect.IntegerHeight() - win_bounds.IntegerHeight() - kWindowBorderWidth;
		}
	}
	return NS_OK;
}

void nsWindow::HideKids(PRBool state)	
{
	for (nsIWidget* kid = mFirstChild; kid; kid = kid->GetNextSibling()) 
	{
		nsWindow *childWidget = static_cast<nsWindow*>(kid);
		nsRect kidrect = ((nsWindow *)kid)->mBounds;
		//Don't bother about invisible
		if (mBounds.Intersects(kidrect))
		{	
			childWidget->Show(!state);
		}
	}
}

//-------------------------------------------------------------------------
//
// Move this component
//
//-------------------------------------------------------------------------
nsresult nsWindow::Move(PRInt32 aX, PRInt32 aY)
{
	// Only perform this check for non-popup windows, since the positioning can
	// in fact change even when the x/y do not.  We always need to perform the
	// check. See bug #97805 for details.
	if (mWindowType != eWindowType_popup && (mBounds.x == aX) && (mBounds.y == aY))
	{
		// Nothing to do, since it is already positioned correctly.
		return NS_OK;    
	}


	// Set cached value for lightweight and printing
	mBounds.x = aX;
	mBounds.y = aY;

	// We may reset children visibility here, but it needs special care 
	// - see comment 18 in Bug 311651. More sofisticated code needed.

	// until we lack separate window and widget, we "cannot" move BWindow without BView
	if (mView && mView->LockLooper())
	{
		if (mView->Parent() || !mView->Window())
			mView->MoveTo(aX, aY);
		else
			((nsWindowBeOS *)mView->Window())->MoveTo(aX, aY);
			
		mView->UnlockLooper();
	}

	OnMove(aX,aY);

	return NS_OK;
}



//-------------------------------------------------------------------------
//
// Resize this component
//
//-------------------------------------------------------------------------
NS_METHOD nsWindow::Resize(PRInt32 aWidth, PRInt32 aHeight, PRBool aRepaint)
{

	if (aWidth < 0 || aHeight < 0)
		return NS_OK;

	mBounds.width  = aWidth;
	mBounds.height = aHeight;
	
	// until we lack separate window and widget, we "cannot" resize BWindow without BView
	if (mView && mView->LockLooper())
	{
		if (mView->Parent() || !mView->Window())
			mView->ResizeTo(aWidth - 1, aHeight - 1);
		else
			((nsWindowBeOS *)mView->Window())->ResizeTo(aWidth - 1, aHeight - 1);

		mView->UnlockLooper();
	}


	OnResize(mBounds);
	if (aRepaint)
		Update();
	return NS_OK;
}

//-------------------------------------------------------------------------
//
// Resize this component
//
//-------------------------------------------------------------------------
NS_METHOD nsWindow::Resize(PRInt32 aX,
                           PRInt32 aY,
                           PRInt32 aWidth,
                           PRInt32 aHeight,
                           PRBool   aRepaint)
{
	Move(aX,aY);
	Resize(aWidth,aHeight,aRepaint);
	return NS_OK;
}

NS_METHOD nsWindow::SetModal(PRBool aModal)
{
	if(!(mView && mView->Window()))
		return NS_ERROR_FAILURE;
	if(aModal)
	{
		window_feel newfeel;
		switch(mBWindowFeel)
		{
			case B_FLOATING_SUBSET_WINDOW_FEEL:
				newfeel = B_MODAL_SUBSET_WINDOW_FEEL;
				break;
 			case B_FLOATING_APP_WINDOW_FEEL:
				newfeel = B_MODAL_APP_WINDOW_FEEL;
				break;
 			case B_FLOATING_ALL_WINDOW_FEEL:
				newfeel = B_MODAL_ALL_WINDOW_FEEL;
				break;				
			default:
				return NS_OK;
		}
		mView->Window()->SetFeel(newfeel);
	}
	else
	{
		mView->Window()->SetFeel(mBWindowFeel);
	}
	return NS_OK;
}
//-------------------------------------------------------------------------
//
// Enable/disable this component
//
//-------------------------------------------------------------------------
NS_METHOD nsWindow::Enable(PRBool aState)
{
	//TODO: Needs real corect implementation in future
	mEnabled = aState;
	return NS_OK;
}


NS_METHOD nsWindow::IsEnabled(PRBool *aState)
{
	NS_ENSURE_ARG_POINTER(aState);
	// looks easy enough, but...
	*aState = mEnabled;
	return NS_OK;
}

//-------------------------------------------------------------------------
//
// Give the focus to this component
//
//-------------------------------------------------------------------------
NS_METHOD nsWindow::SetFocus(PRBool aRaise)
{
	//
	// Switch to the "main gui thread" if necessary... This method must
	// be executed on the "gui thread"...
	//
	nsToolkit* toolkit = (nsToolkit *)mToolkit;
	if (toolkit && !toolkit->IsGuiThread()) 
	{
		nsCOMPtr<nsIWidget> widgetProxy;
		nsresult rv = NS_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
										NS_GET_IID(nsIWidget),
										this, 
										NS_PROXY_SYNC | NS_PROXY_ALWAYS, 
										getter_AddRefs(widgetProxy));
	
		if (NS_FAILED(rv))
			return rv;
		return widgetProxy->SetFocus(aRaise);
	}
	
	// Don't set focus on disabled widgets or popups
	if (!mEnabled || eWindowType_popup == mWindowType)
		return NS_OK;
		
	if (mView && mView->LockLooper())
	{
		if (mView->Window() && 
		    aRaise == PR_TRUE &&
		    eWindowType_popup != mWindowType && 
			  !mView->Window()->IsActive() && 
			  gLastActiveWindow != mView->Window())
			mView->Window()->Activate(true);
			
		mView->MakeFocus(true);
		mView->UnlockLooper();
		DispatchFocus(NS_GOTFOCUS);
	}

	return NS_OK;
}

//-------------------------------------------------------------------------
//
// Get this component size and position in screen coordinates
//
//-------------------------------------------------------------------------    
NS_IMETHODIMP nsWindow::GetScreenBounds(nsRect &aRect)
{
	// A window's Frame() value is cached, so locking is not needed
	if (mView && mView->Window()) 
	{
		BRect r = mView->Window()->Frame();
		aRect.x = nscoord(r.left);
		aRect.y = nscoord(r.top);
		aRect.width  = r.IntegerWidth()+1;
		aRect.height = r.IntegerHeight()+1;
	} 
	else 
	{
		aRect = mBounds;
	}
	return NS_OK;
}  

//-------------------------------------------------------------------------
//
// Set the background/foreground color
//
//-------------------------------------------------------------------------
NS_METHOD nsWindow::SetBackgroundColor(const nscolor &aColor)
{
	nsBaseWidget::SetBackgroundColor(aColor);

	// We set the background of toplevel windows so that resizing doesn't show thru
	// to Desktop and resizing artifacts. Child windows has transparent background.
	if (!mIsTopWidgetWindow)
		return NS_OK;

	if (mView && mView->LockLooper())
	{
		mView->SetViewColor(NS_GET_R(aColor), NS_GET_G(aColor), NS_GET_B(aColor), NS_GET_A(aColor));
		mView->UnlockLooper();
	}
	return NS_OK;
}


//-------------------------------------------------------------------------
//
// Set this component cursor
//
//-------------------------------------------------------------------------

NS_METHOD nsWindow::SetCursor(nsCursor aCursor)
{
	if (!mView)
		return NS_ERROR_FAILURE;

	// Only change cursor if it's changing
	if (aCursor != mCursor) 
	{
		BCursor const *newCursor = B_CURSOR_SYSTEM_DEFAULT;
		if (be_app->IsCursorHidden())
			be_app->ShowCursor();
		
		// Check to see if the array has been loaded, if not, do it.
		if (gCursorArray.Count() == 0) 
		{
			gCursorArray.InsertElementAt((void*) new BCursor(cursorHyperlink),0);
			gCursorArray.InsertElementAt((void*) new BCursor(cursorHorizontalDrag),1);
			gCursorArray.InsertElementAt((void*) new BCursor(cursorVerticalDrag),2);
			gCursorArray.InsertElementAt((void*) new BCursor(cursorUpperLeft),3);
			gCursorArray.InsertElementAt((void*) new BCursor(cursorLowerRight),4);
			gCursorArray.InsertElementAt((void*) new BCursor(cursorUpperRight),5);
			gCursorArray.InsertElementAt((void*) new BCursor(cursorLowerLeft),6);
			gCursorArray.InsertElementAt((void*) new BCursor(cursorCrosshair),7);
			gCursorArray.InsertElementAt((void*) new BCursor(cursorHelp),8);
			gCursorArray.InsertElementAt((void*) new BCursor(cursorGrab),9);
			gCursorArray.InsertElementAt((void*) new BCursor(cursorGrabbing),10);
			gCursorArray.InsertElementAt((void*) new BCursor(cursorCopy),11);
			gCursorArray.InsertElementAt((void*) new BCursor(cursorAlias),12);
			gCursorArray.InsertElementAt((void*) new BCursor(cursorWatch2),13);
			gCursorArray.InsertElementAt((void*) new BCursor(cursorCell),14);
			gCursorArray.InsertElementAt((void*) new BCursor(cursorZoomIn),15);
			gCursorArray.InsertElementAt((void*) new BCursor(cursorZoomOut),16);
			gCursorArray.InsertElementAt((void*) new BCursor(cursorLeft),17);
			gCursorArray.InsertElementAt((void*) new BCursor(cursorRight),18);
			gCursorArray.InsertElementAt((void*) new BCursor(cursorTop),19);
			gCursorArray.InsertElementAt((void*) new BCursor(cursorBottom),20);
		}

		switch (aCursor) 
		{
			case eCursor_standard:
			case eCursor_move:
				newCursor = B_CURSOR_SYSTEM_DEFAULT;
				break;
	
			case eCursor_select:
				newCursor = B_CURSOR_I_BEAM;
				break;
	
			case eCursor_hyperlink:
				newCursor = (BCursor *)gCursorArray.SafeElementAt(0);
				break;
	
			case eCursor_n_resize:
				newCursor = (BCursor *)gCursorArray.SafeElementAt(19);
				break;

			case eCursor_s_resize:
				newCursor = (BCursor *)gCursorArray.SafeElementAt(20);
				break;
	
			case eCursor_w_resize:
				newCursor = (BCursor *)gCursorArray.SafeElementAt(17);
				break;

			case eCursor_e_resize:
				newCursor = (BCursor *)gCursorArray.SafeElementAt(18);
				break;
	
			case eCursor_nw_resize:
				newCursor = (BCursor *)gCursorArray.SafeElementAt(3);
				break;
	
			case eCursor_se_resize:
				newCursor = (BCursor *)gCursorArray.SafeElementAt(4);
				break;
	
			case eCursor_ne_resize:
				newCursor = (BCursor *)gCursorArray.SafeElementAt(5);
				break;
	
			case eCursor_sw_resize:
				newCursor = (BCursor *)gCursorArray.SafeElementAt(6);
				break;
	
			case eCursor_crosshair:
				newCursor = (BCursor *)gCursorArray.SafeElementAt(7);
				break;
	
			case eCursor_help:
				newCursor = (BCursor *)gCursorArray.SafeElementAt(8);
				break;
	
			case eCursor_copy:
				newCursor = (BCursor *)gCursorArray.SafeElementAt(11);
				break;
	
			case eCursor_alias:
				newCursor = (BCursor *)gCursorArray.SafeElementAt(12);
				break;

			case eCursor_context_menu:
				// XXX: No suitable cursor, needs implementing
				break;
				
			case eCursor_cell:
				newCursor = (BCursor *)gCursorArray.SafeElementAt(14);
				break;

			case eCursor_grab:
				newCursor = (BCursor *)gCursorArray.SafeElementAt(9);
				break;
	
			case eCursor_grabbing:
				newCursor = (BCursor *)gCursorArray.SafeElementAt(10);
				break;
	
			case eCursor_wait:
			case eCursor_spinning:
				newCursor = (BCursor *)gCursorArray.SafeElementAt(13);
				break;
	
			case eCursor_zoom_in:
				newCursor = (BCursor *)gCursorArray.SafeElementAt(15);
				break;

			case eCursor_zoom_out:
				newCursor = (BCursor *)gCursorArray.SafeElementAt(16);
				break;

			case eCursor_not_allowed:
			case eCursor_no_drop:
				// XXX: No suitable cursor, needs implementing
				break;

			case eCursor_col_resize:
				// XXX not 100% appropriate perhaps
				newCursor = (BCursor *)gCursorArray.SafeElementAt(1);
				break;

			case eCursor_row_resize:
				// XXX not 100% appropriate perhaps
				newCursor = (BCursor *)gCursorArray.SafeElementAt(2);
				break;

			case eCursor_vertical_text:
				// XXX not 100% appropriate perhaps
				newCursor = B_CURSOR_I_BEAM;
				break;

			case eCursor_all_scroll:
				// XXX: No suitable cursor, needs implementing
				break;

			case eCursor_nesw_resize:
				// XXX not 100% appropriate perhaps
				newCursor = (BCursor *)gCursorArray.SafeElementAt(1);
				break;

			case eCursor_nwse_resize:
				// XXX not 100% appropriate perhaps
				newCursor = (BCursor *)gCursorArray.SafeElementAt(1);
				break;

			case eCursor_ns_resize:
				newCursor = (BCursor *)gCursorArray.SafeElementAt(2);
				break;

			case eCursor_ew_resize:
				newCursor = (BCursor *)gCursorArray.SafeElementAt(1);
				break;

			case eCursor_none:
				be_app->HideCursor();
				break;

			default:
				NS_ASSERTION(0, "Invalid cursor type");
				break;
		}
		NS_ASSERTION(newCursor != nsnull, "Cursor not stored in array properly!");
		mCursor = aCursor;
		be_app->SetCursor(newCursor, true);
	}
	return NS_OK;
}

//-------------------------------------------------------------------------
//
// Invalidate this component visible area
//
//-------------------------------------------------------------------------
NS_METHOD nsWindow::Invalidate(PRBool aIsSynchronous)
{
	nsresult rv = NS_ERROR_FAILURE;
	// Asynchronous painting is performed with via nsViewBeOS::Draw() call and its message queue. 
	// All update rects are collected in nsViewBeOS member  "paintregion".
	// Flushing of paintregion happens in nsViewBeOS::GetPaintRegion(),
	// cleanup  - in nsViewBeOS::Validate(), called in OnPaint().
	BRegion reg;
	reg.MakeEmpty();
	if (mView && mView->LockLooper())
	{
		if (PR_TRUE == aIsSynchronous)
		{
			mView->paintregion.Include(mView->Bounds());
			reg.Include(mView->Bounds());
		}
		else
		{
			mView->Draw(mView->Bounds());
			rv = NS_OK;
		}
		mView->UnlockLooper();
	}
	// Instant repaint.
	if (PR_TRUE == aIsSynchronous)
		rv = OnPaint(&reg);
	return rv;
}

//-------------------------------------------------------------------------
//
// Invalidate this component visible area
//
//-------------------------------------------------------------------------
NS_METHOD nsWindow::Invalidate(const nsRect & aRect, PRBool aIsSynchronous)
{
	nsresult rv = NS_ERROR_FAILURE;
	// Very temporary region for double accounting.
	BRegion reg;
	reg.MakeEmpty();
	if (mView && mView->LockLooper()) 
	{
		BRect	r(aRect.x, 
				aRect.y, 
				aRect.x + aRect.width - 1, 
				aRect.y + aRect.height - 1);
		if (PR_TRUE == aIsSynchronous)
		{
			mView->paintregion.Include(r);
			reg.Include(r);
		}
		else
		{
			// we use Draw() instead direct addition to paintregion,
			// as it sets queue of notification messages for painting.
			mView->Draw(r);
			rv = NS_OK;
		}
		mView->UnlockLooper();
	}
	// Instant repaint - for given rect only. 
	// Don't repaint area which isn't marked here for synchronous repaint explicitly.
	// BRegion "reg" (equal to aRect) will be substracted from paintregion in OnPaint().
	if (PR_TRUE == aIsSynchronous)
		rv = OnPaint(&reg);
	return rv;
}

//-------------------------------------------------------------------------
//
// Invalidate this component visible area
//
//-------------------------------------------------------------------------
NS_IMETHODIMP nsWindow::InvalidateRegion(const nsIRegion *aRegion, PRBool aIsSynchronous)
{
	
	nsRegionRectSet *rectSet = nsnull;
	if (!aRegion)
		return NS_ERROR_FAILURE;
	nsresult rv = ((nsIRegion *)aRegion)->GetRects(&rectSet);
	if (NS_FAILED(rv))
		return rv;
	BRegion reg;
	reg.MakeEmpty();
	if (mView && mView->LockLooper())
	{
		for (PRUint32 i=0; i< rectSet->mRectsLen; ++i)
		{
			BRect br(rectSet->mRects[i].x, rectSet->mRects[i].y,
					rectSet->mRects[i].x + rectSet->mRects[i].width-1,
					rectSet->mRects[i].y + rectSet->mRects[i].height -1);
			if (PR_TRUE == aIsSynchronous)
			{
				mView->paintregion.Include(br);
				reg.Include(br);
			}
			else
			{
				mView->Draw(br);
				rv = NS_OK;
			}
		}
		mView->UnlockLooper();
	}
	// Instant repaint - for given region only. 
	// BRegion "reg"(equal to aRegion) will be substracted from paintregion in OnPaint().
	if (PR_TRUE == aIsSynchronous)
		rv = OnPaint(&reg);

	return rv;
}

//-------------------------------------------------------------------------
//
// Force a synchronous repaint of the window
//
//-------------------------------------------------------------------------
NS_IMETHODIMP nsWindow::Update()
{
	nsresult rv = NS_ERROR_FAILURE;
	//Switching scrolling trigger off
	mIsScrolling = PR_FALSE;
	if (mWindowType == eWindowType_child)
		return NS_OK;
	BRegion reg;
	reg.MakeEmpty();
	if(mView && mView->LockLooper())
	{
		//Flushing native pending updates if any
		if (mView->Window())
			mView->Window()->UpdateIfNeeded();
		// Let app_server to invalidate
		mView->Invalidate();
		bool nonempty = mView->GetPaintRegion(&reg);
		mView->UnlockLooper();
		// Look if native update calls above filled update region and paint it
		if (nonempty)
			rv = OnPaint(&reg);
	}
	return rv;
}

//-------------------------------------------------------------------------
//
// Return some native data according to aDataType
//
//-------------------------------------------------------------------------
void* nsWindow::GetNativeData(PRUint32 aDataType)
{
	if (!mView)
		return NULL;	
	switch(aDataType) 
	{
		case NS_NATIVE_WINDOW:
			return (void *)(mView->Window());
		case NS_NATIVE_WIDGET:
		case NS_NATIVE_PLUGIN_PORT:
			return (void *)((nsViewBeOS *)mView);
		case NS_NATIVE_GRAPHIC:
			return (void *)((BView *)mView);
		case NS_NATIVE_COLORMAP:
		default:
			break;
	}
	return NULL;
}

//-------------------------------------------------------------------------
//
// Set the colormap of the window
//
//-------------------------------------------------------------------------
NS_METHOD nsWindow::SetColorMap(nsColorMap *aColorMap)
{
	NS_WARNING("nsWindow::SetColorMap - not implemented");
	return NS_OK;
}


//-------------------------------------------------------------------------
//
// Scroll the bits of a window
//
//-------------------------------------------------------------------------
NS_METHOD nsWindow::Scroll(PRInt32 aDx, PRInt32 aDy, nsRect *aClipRect)
{
	// Switching trigger on
	mIsScrolling = PR_TRUE;
	//Preventing main view invalidation loop-chain  when children are moving
	//by by hiding children nsWidgets.
	//Maybe this method must be used wider, in move and resize chains
	// and implemented in BeginResizingChildren or in Reset*Visibility() methods
	//Children will be unhidden in ::Update() when called by other than gkview::Scroll() method.
	HideKids(PR_TRUE);
	if (mView && mView->LockLooper())
	{
		// Kill any attempt to invalidate until scroll is finished
		mView->SetVisible(false);
		
		BRect src;
		BRect b = mView->Bounds();

		if (aClipRect)
		{
			src.left = aClipRect->x;
			src.top = aClipRect->y;
			src.right = aClipRect->XMost() - 1;
			src.bottom = aClipRect->YMost() - 1;
		}
		else
		{
			src = b;
		}
		// Restricting source by on-screen part of BView
		if (mView->Window())
		{
			BRect screenframe = mView->ConvertFromScreen(BScreen(mView->Window()).Frame());
			src = src & screenframe;
			if (mView->Parent())
			{
				BRect parentframe = mView->ConvertFromParent(mView->Parent()->Frame());
				src = src & parentframe;
			}
		}

		BRegion	invalid;
		invalid.Include(src);
		// Next source clipping check, for same level siblings
		if ( BView *v = mView->Parent() )
		{
			for (BView *child = v->ChildAt(0); child; child = child->NextSibling() )
			{
				BRect siblingframe = mView->ConvertFromParent(child->Frame());
				if (child != mView && child->Parent() != mView)
				{
					invalid.Exclude(siblingframe);
					mView->paintregion.Exclude(siblingframe);
				}
			}
			src = invalid.Frame();
		}

		// make sure we only reference visible bits
		// so we don't trigger a BView invalidate

		if (src.left + aDx < 0)
			src.left = -aDx;
		if (src.right + aDx > b.right)
			src.right = b.right - aDx;
		if (src.top + aDy < 0)
			src.top = -aDy;
		if (src.bottom + aDy > b.bottom)
			src.bottom = b.bottom - aDy;
		
		BRect dest = src.OffsetByCopy(aDx, aDy);
		mView->ConstrainClippingRegion(&invalid);
		// Moving visible content 
		if (src.IsValid() && dest.IsValid())
			mView->CopyBits(src, dest);

		invalid.Exclude(dest);	
		// Native paintregion needs shifting too, it is very important action
		// (as app_server doesn't know about Mozilla viewmanager tricks) -
		// it allows proper update after scroll for areas covered by other windows.
		mView->paintregion.OffsetBy(aDx, aDy);
		mView->ConstrainClippingRegion(&invalid);
		// Time to silently move now invisible children
		for (nsIWidget* kid = mFirstChild; kid; kid = kid->GetNextSibling()) 
		{
			nsWindow *childWidget = static_cast<nsWindow*>(kid);
			// No need to Lock/UnlockLooper with GetBounds() and Move() methods
			// using cached values and native MoveBy() instead
			nsRect bounds = childWidget->mBounds;
			bounds.x += aDx;
			bounds.y += aDy; 
			childWidget->Move(bounds.x, bounds.y);
			BView *child = ((BView *)kid->GetNativeData(NS_NATIVE_WIDGET));
			if (child)
				mView->paintregion.Exclude(child->Frame());
		}
		
		// Painting calculated region now,
		// letting Update() to paint remaining content of paintregion
		OnPaint(&invalid);
		HideKids(PR_FALSE);
		// re-allow updates
		mView->SetVisible(true);
		mView->UnlockLooper();
	}
	return NS_OK;
}


//-------------------------------------------------------------------------
//
// Every function that needs a thread switch goes through this function
// by calling SendMessage (..WM_CALLMETHOD..) in nsToolkit::CallMethod.
//
//-------------------------------------------------------------------------
bool nsWindow::CallMethod(MethodInfo *info)
{
	bool bRet = TRUE;

	switch (info->methodId)
	{
	case nsSwitchToUIThread::CLOSEWINDOW :
		{
			NS_ASSERTION(info->nArgs == 0, "Wrong number of arguments to CallMethod");
			if (eWindowType_popup != mWindowType && eWindowType_child != mWindowType)
				DealWithPopups(nsSwitchToUIThread::CLOSEWINDOW,nsPoint(0,0));

			// Bit more Kung-fu. We do care ourselves about children destroy notofication.
			// Including those floating dialogs we added to Gecko hierarchy in StandardWindowCreate()

			for (nsIWidget* kid = mFirstChild; kid; kid = kid->GetNextSibling()) 
			{
				nsWindow *childWidget = static_cast<nsWindow*>(kid);
				BWindow* kidwindow = (BWindow *)kid->GetNativeData(NS_NATIVE_WINDOW);
				if (kidwindow)
				{
					// PostMessage() is unsafe, so using BMessenger
					BMessenger bm(kidwindow);
					bm.SendMessage(B_QUIT_REQUESTED);
				}
			}
			DispatchStandardEvent(NS_DESTROY);
		}
		break;

#ifdef DEBUG_FOCUS
	case nsSwitchToUIThread::GOT_FOCUS:
		NS_ASSERTION(info->nArgs == 1, "Wrong number of arguments to CallMethod");
		if (!mEnabled)
			return false;
		if ((uint32)info->args[0] != (uint32)mView)
			printf("Wrong view to get focus\n");*/
		break;
#endif
	case nsSwitchToUIThread::KILL_FOCUS:
		NS_ASSERTION(info->nArgs == 1, "Wrong number of arguments to CallMethod");
		if ((uint32)info->args[0] == (uint32)mView)
			DispatchFocus(NS_LOSTFOCUS);
#ifdef DEBUG_FOCUS
		else
			printf("Wrong view to de-focus\n");
#endif
#if defined BeIME
		nsIMEBeOS::GetIME()->DispatchCancelIME();
		if (mView && mView->LockLooper())
 		{
 			mView->SetFlags(mView->Flags() & ~B_NAVIGABLE);
 			mView->UnlockLooper();
 		}
#endif
		break;

	case nsSwitchToUIThread::BTNCLICK :
		{
			NS_ASSERTION(info->nArgs == 6, "Wrong number of arguments to CallMethod");
			if (!mEnabled)
				return false;
			// close popup when clicked outside of the popup window
			uint32 eventID = ((int32 *)info->args)[0];
			PRBool rollup = PR_FALSE;

			if (eventID == NS_MOUSE_BUTTON_DOWN &&
			        mView && mView->LockLooper())
			{
				BPoint p(((int32 *)info->args)[1], ((int32 *)info->args)[2]);
				mView->ConvertToScreen(&p);
				rollup = DealWithPopups(nsSwitchToUIThread::ONMOUSE, nsPoint(p.x, p.y));
				mView->UnlockLooper();
			}
			// Drop click event - bug 314330
			if (rollup)
				return false;
			DispatchMouseEvent(((int32 *)info->args)[0],
			                   nsPoint(((int32 *)info->args)[1], ((int32 *)info->args)[2]),
			                   ((int32 *)info->args)[3],
			                   ((int32 *)info->args)[4],
			                   ((int32 *)info->args)[5]);

			if (((int32 *)info->args)[0] == NS_MOUSE_BUTTON_DOWN &&
			    ((int32 *)info->args)[5] == nsMouseEvent::eRightButton)
			{
				DispatchMouseEvent (NS_CONTEXTMENU,
				                    nsPoint(((int32 *)info->args)[1], ((int32 *)info->args)[2]),
				                    ((int32 *)info->args)[3],
				                    ((int32 *)info->args)[4],
				                    ((int32 *)info->args)[5]);
			}
		}
		break;

	case nsSwitchToUIThread::ONWHEEL :
		{
			NS_ASSERTION(info->nArgs == 1, "Wrong number of arguments to CallMethod");
			// avoid mistargeting
			if ((uint32)info->args[0] != (uint32)mView)
				return false;
			BPoint cursor(0,0);
			uint32 buttons;
			BPoint delta;
			if (mView && mView->LockLooper())
			{
				mView->GetMouse(&cursor, &buttons, false);
				delta = mView->GetWheel();
				mView->UnlockLooper();
			}
			else
				return false;
			// BeOS TwoWheel input-filter is bit buggy atm, generating sometimes X-wheel with no reason,
			// so we're setting priority for Y-wheel.
			// Also hardcoding here _system_ scroll-step value to 3 lines.
			if (nscoord(delta.y) != 0)
			{
				OnWheel(nsMouseScrollEvent::kIsVertical, buttons, cursor, nscoord(delta.y)*3);
			}
			else if(nscoord(delta.x) != 0)
				OnWheel(nsMouseScrollEvent::kIsHorizontal, buttons, cursor, nscoord(delta.x)*3);
		}
		break;

	case nsSwitchToUIThread::ONKEY :
		NS_ASSERTION(info->nArgs == 6, "Wrong number of arguments to CallMethod");
		if (((int32 *)info->args)[0] == NS_KEY_DOWN)
		{
			OnKeyDown(((int32 *)info->args)[0],
			          (const char *)(&((uint32 *)info->args)[1]), ((int32 *)info->args)[2],
			          ((uint32 *)info->args)[3], ((uint32 *)info->args)[4], ((int32 *)info->args)[5]);
		}
		else
		{
			if (((int32 *)info->args)[0] == NS_KEY_UP)
			{
				OnKeyUp(((int32 *)info->args)[0],
				        (const char *)(&((uint32 *)info->args)[1]), ((int32 *)info->args)[2],
				        ((uint32 *)info->args)[3], ((uint32 *)info->args)[4], ((int32 *)info->args)[5]);
			}
		}
		break;

	case nsSwitchToUIThread::ONPAINT :
		NS_ASSERTION(info->nArgs == 1, "Wrong number of arguments to CallMethod");
		{
			if ((uint32)mView != ((uint32 *)info->args)[0])
				return false;
			BRegion reg;
			reg.MakeEmpty();
			if(mView && mView->LockLooper())
			{
				bool nonempty = mView->GetPaintRegion(&reg);
				mView->UnlockLooper();
				if (nonempty)
					OnPaint(&reg);
			}
		}
		break;

	case nsSwitchToUIThread::ONRESIZE :
		{
			NS_ASSERTION(info->nArgs == 0, "Wrong number of arguments to CallMethod");
			if (eWindowType_popup != mWindowType && eWindowType_child != mWindowType)
				DealWithPopups(nsSwitchToUIThread::ONRESIZE,nsPoint(0,0));
			// This should be called only from BWindow::FrameResized()
			if (!mIsTopWidgetWindow  || !mView  || !mView->Window())
				return false;
			
			nsRect r(mBounds);
			if (mView->LockLooper())
			{
				BRect br = mView->Frame();
				r.x = nscoord(br.left);
				r.y = nscoord(br.top);
				r.width  = br.IntegerWidth() + 1;
				r.height = br.IntegerHeight() + 1;
				((nsWindowBeOS *)mView->Window())->fJustGotBounds = true;
				mView->UnlockLooper();
			}
			OnResize(r);
		}
		break;

	case nsSwitchToUIThread::ONMOUSE :
		{
			NS_ASSERTION(info->nArgs == 4, "Wrong number of arguments to CallMethod");
			if (!mEnabled)
				return false;
			DispatchMouseEvent(((int32 *)info->args)[0],
			                   nsPoint(((int32 *)info->args)[1], ((int32 *)info->args)[2]),
			                   0,
		    	               ((int32 *)info->args)[3]);
		}
		break;

	case nsSwitchToUIThread::ONDROP :
		{
			NS_ASSERTION(info->nArgs == 4, "Wrong number of arguments to CallMethod");

			nsDragEvent event(PR_TRUE, (int32)  info->args[0], this);
			nsPoint point(((int32 *)info->args)[1], ((int32 *)info->args)[2]);
			InitEvent (event, &point);
			uint32 mod = (uint32) info->args[3];
			event.isShift   = mod & B_SHIFT_KEY;
			event.isControl = mod & B_CONTROL_KEY;
			event.isAlt     = mod & B_COMMAND_KEY;
			event.isMeta     = mod & B_OPTION_KEY;

			// Setting drag action, must be done before event dispatch
			nsCOMPtr<nsIDragService> dragService = do_GetService(kCDragServiceCID);
			if (dragService)
			{
				nsCOMPtr<nsIDragSession> dragSession;
				dragService->GetCurrentSession(getter_AddRefs(dragSession));
				if (dragSession)
				{
					// Original action mask stored in dragsession.
					// For native events such mask must be set in nsDragServiceBeOS::UpdateDragMessageIfNeeded()
	
					PRUint32 action_mask = 0;
					dragSession->GetDragAction(&action_mask);
					PRUint32 action = nsIDragService::DRAGDROP_ACTION_MOVE;
					if (mod & B_OPTION_KEY)
					{
						if (mod & B_COMMAND_KEY)
							action = nsIDragService::DRAGDROP_ACTION_LINK & action_mask;
						else
							action = nsIDragService::DRAGDROP_ACTION_COPY & action_mask;
					}
					dragSession->SetDragAction(action);
				}
			}
			DispatchWindowEvent(&event);
			NS_RELEASE(event.widget);

			if (dragService)
				dragService->EndDragSession(PR_TRUE);
		}
		break;

	case nsSwitchToUIThread::ONACTIVATE:
		NS_ASSERTION(info->nArgs == 2, "Wrong number of arguments to CallMethod");
		if (!mEnabled || eWindowType_popup == mWindowType || 0 == mView->Window())
			return false;
		if ((BWindow *)info->args[1] != mView->Window())
			return false;
		if (mEventCallback || eWindowType_child == mWindowType )
		{
			bool active = (bool)info->args[0];
			if (!active) 
			{
				if (eWindowType_dialog == mWindowType || 
				    eWindowType_toplevel == mWindowType)
					DealWithPopups(nsSwitchToUIThread::ONACTIVATE,nsPoint(0,0));
				//Testing if BWindow is really deactivated.
				if (!mView->Window()->IsActive())
				{
					// BeOS is poor in windows hierarchy and variations support. In lot of aspects.
					// Here is workaround for flacky Activate() handling for B_FLOATING windows.
					// We should force parent (de)activation to allow main window to regain control after closing floating dialog.
					if (mWindowParent &&  mView->Window()->IsFloating())
						mWindowParent->DispatchFocus(NS_ACTIVATE);

					DispatchFocus(NS_DEACTIVATE);
#if defined(BeIME)
					nsIMEBeOS::GetIME()->DispatchCancelIME();
#endif
				}
			} 
			else 
			{

				if (mView->Window()->IsActive())
				{
					// See comment above.
					if (mWindowParent &&  mView->Window()->IsFloating())
						mWindowParent->DispatchFocus(NS_DEACTIVATE);
					
					DispatchFocus(NS_ACTIVATE);
					if (mView && mView->Window())
						gLastActiveWindow = mView->Window();
				}
			}
		}
		break;

	case nsSwitchToUIThread::ONMOVE:
		{
			NS_ASSERTION(info->nArgs == 0, "Wrong number of arguments to CallMethod");
			nsRect r;
			// We use this only for tracking whole window moves
			GetScreenBounds(r);		
			if (eWindowType_popup != mWindowType && eWindowType_child != mWindowType)
				DealWithPopups(nsSwitchToUIThread::ONMOVE,nsPoint(0,0));
			OnMove(r.x, r.y);
		}
		break;
		
	case nsSwitchToUIThread::ONWORKSPACE:
		{
			NS_ASSERTION(info->nArgs == 2, "Wrong number of arguments to CallMethod");
			if (eWindowType_popup != mWindowType && eWindowType_child != mWindowType)
				DealWithPopups(nsSwitchToUIThread::ONWORKSPACE,nsPoint(0,0));
		}
		break;

#if defined(BeIME)
 	case nsSwitchToUIThread::ONIME:
 		//No assertion used, as number of arguments varies here
 		if (mView && mView->LockLooper())
 		{
 			mView->SetFlags(mView->Flags() | B_NAVIGABLE);
 			mView->UnlockLooper();
 		}
 		nsIMEBeOS::GetIME()->RunIME(info->args, this, mView);
 		break;
#endif
		default:
			bRet = FALSE;
			break;
		
	}

	return bRet;
}

//-------------------------------------------------------------------------
//
// Key code translation related data
//
//-------------------------------------------------------------------------

struct nsKeyConverter {
	int vkCode; // Platform independent key code
	char bekeycode; // BeOS key code
};

//
// Netscape keycodes are defined in widget/public/nsGUIEvent.h
// BeOS keycodes can be viewd at
// http://www.be.com/documentation/be_book/Keyboard/KeyboardKeyCodes.html
//

struct nsKeyConverter nsKeycodesBeOS[] = {
	        //  { NS_VK_CANCEL,     GDK_Cancel },
	        { NS_VK_BACK,       0x1e },
	        { NS_VK_TAB,        0x26 },
	        //  { NS_VK_TAB,        GDK_ISO_Left_Tab },
	        //  { NS_VK_CLEAR,      GDK_Clear },
	        { NS_VK_RETURN,     0x47 },
	        { NS_VK_SHIFT,      0x4b },
	        { NS_VK_SHIFT,      0x56 },
	        { NS_VK_CONTROL,    0x5c },
	        { NS_VK_CONTROL,    0x60 },
	        { NS_VK_ALT,        0x5d },
	        { NS_VK_ALT,        0x5f },
	        { NS_VK_PAUSE,      0x22 },
	        { NS_VK_CAPS_LOCK,  0x3b },
	        { NS_VK_ESCAPE,     0x1 },
	        { NS_VK_SPACE,      0x5e },
	        { NS_VK_PAGE_UP,    0x21 },
	        { NS_VK_PAGE_DOWN,  0x36 },
	        { NS_VK_END,        0x35 },
	        { NS_VK_HOME,       0x20 },
	        { NS_VK_LEFT,       0x61 },
	        { NS_VK_UP,         0x57 },
	        { NS_VK_RIGHT,      0x63 },
	        { NS_VK_DOWN,       0x62 },
	        { NS_VK_PRINTSCREEN, 0xe },
	        { NS_VK_INSERT,     0x1f },
	        { NS_VK_DELETE,     0x34 },

	        // The "Windows Key"
	        { NS_VK_META,       0x66 },
	        { NS_VK_META,       0x67 },

	        // keypad keys (constant keys)
	        { NS_VK_MULTIPLY,   0x24 },
	        { NS_VK_ADD,        0x3a },
	        //  { NS_VK_SEPARATOR,   }, ???
	        { NS_VK_SUBTRACT,   0x25 },
	        { NS_VK_DIVIDE,     0x23 },
	        { NS_VK_RETURN,     0x5b },

	        { NS_VK_COMMA,      0x53 },
	        { NS_VK_PERIOD,     0x54 },
	        { NS_VK_SLASH,      0x55 },
	        { NS_VK_BACK_SLASH, 0x33 },
	        { NS_VK_BACK_SLASH, 0x6a }, // got this code on japanese keyboard
	        { NS_VK_BACK_SLASH, 0x6b }, // got this code on japanese keyboard
	        { NS_VK_BACK_QUOTE, 0x11 },
	        { NS_VK_OPEN_BRACKET, 0x31 },
	        { NS_VK_CLOSE_BRACKET, 0x32 },
	        { NS_VK_SEMICOLON, 0x45 },
	        { NS_VK_QUOTE, 0x46 },

	        // NS doesn't have dash or equals distinct from the numeric keypad ones,
	        // so we'll use those for now.  See bug 17008:
	        { NS_VK_SUBTRACT, 0x1c },
	        { NS_VK_EQUALS, 0x1d },

	        { NS_VK_F1, B_F1_KEY },
	        { NS_VK_F2, B_F2_KEY },
	        { NS_VK_F3, B_F3_KEY },
	        { NS_VK_F4, B_F4_KEY },
	        { NS_VK_F5, B_F5_KEY },
	        { NS_VK_F6, B_F6_KEY },
	        { NS_VK_F7, B_F7_KEY },
	        { NS_VK_F8, B_F8_KEY },
	        { NS_VK_F9, B_F9_KEY },
	        { NS_VK_F10, B_F10_KEY },
	        { NS_VK_F11, B_F11_KEY },
	        { NS_VK_F12, B_F12_KEY },

	        { NS_VK_1, 0x12 },
	        { NS_VK_2, 0x13 },
	        { NS_VK_3, 0x14 },
	        { NS_VK_4, 0x15 },
	        { NS_VK_5, 0x16 },
	        { NS_VK_6, 0x17 },
	        { NS_VK_7, 0x18 },
	        { NS_VK_8, 0x19 },
	        { NS_VK_9, 0x1a },
	        { NS_VK_0, 0x1b },

	        { NS_VK_A, 0x3c },
	        { NS_VK_B, 0x50 },
	        { NS_VK_C, 0x4e },
	        { NS_VK_D, 0x3e },
	        { NS_VK_E, 0x29 },
	        { NS_VK_F, 0x3f },
	        { NS_VK_G, 0x40 },
	        { NS_VK_H, 0x41 },
	        { NS_VK_I, 0x2e },
	        { NS_VK_J, 0x42 },
	        { NS_VK_K, 0x43 },
	        { NS_VK_L, 0x44 },
	        { NS_VK_M, 0x52 },
	        { NS_VK_N, 0x51 },
	        { NS_VK_O, 0x2f },
	        { NS_VK_P, 0x30 },
	        { NS_VK_Q, 0x27 },
	        { NS_VK_R, 0x2a },
	        { NS_VK_S, 0x3d },
	        { NS_VK_T, 0x2b },
	        { NS_VK_U, 0x2d },
	        { NS_VK_V, 0x4f },
	        { NS_VK_W, 0x28 },
	        { NS_VK_X, 0x4d },
	        { NS_VK_Y, 0x2c },
	        { NS_VK_Z, 0x4c }
        };

// keycode of keypad when num-locked
struct nsKeyConverter nsKeycodesBeOSNumLock[] = {
	        { NS_VK_NUMPAD0, 0x64 },
	        { NS_VK_NUMPAD1, 0x58 },
	        { NS_VK_NUMPAD2, 0x59 },
	        { NS_VK_NUMPAD3, 0x5a },
	        { NS_VK_NUMPAD4, 0x48 },
	        { NS_VK_NUMPAD5, 0x49 },
	        { NS_VK_NUMPAD6, 0x4a },
	        { NS_VK_NUMPAD7, 0x37 },
	        { NS_VK_NUMPAD8, 0x38 },
	        { NS_VK_NUMPAD9, 0x39 },
	        { NS_VK_DECIMAL, 0x65 }
        };

// keycode of keypad when not num-locked
struct nsKeyConverter nsKeycodesBeOSNoNumLock[] = {
	        { NS_VK_LEFT,       0x48 },
	        { NS_VK_RIGHT,      0x4a },
	        { NS_VK_UP,         0x38 },
	        { NS_VK_DOWN,       0x59 },
	        { NS_VK_PAGE_UP,    0x39 },
	        { NS_VK_PAGE_DOWN,  0x5a },
	        { NS_VK_HOME,       0x37 },
	        { NS_VK_END,        0x58 },
	        { NS_VK_INSERT,     0x64 },
	        { NS_VK_DELETE,     0x65 }
        };

//-------------------------------------------------------------------------
//
// Translate key code
// Input is BeOS keyboard key-code; output is in NS_VK format
//
//-------------------------------------------------------------------------

static int TranslateBeOSKeyCode(int32 bekeycode, bool isnumlock)
{
#ifdef KB_DEBUG
	printf("TranslateBeOSKeyCode: bekeycode = 0x%x\n",bekeycode);
#endif
	int i;
	int length = sizeof(nsKeycodesBeOS) / sizeof(struct nsKeyConverter);
	int length_numlock = sizeof(nsKeycodesBeOSNumLock) / sizeof(struct nsKeyConverter);
	int length_nonumlock = sizeof(nsKeycodesBeOSNoNumLock) / sizeof(struct nsKeyConverter);

	// key code conversion
	for (i = 0; i < length; i++)
	{
		if (nsKeycodesBeOS[i].bekeycode == bekeycode)
			return(nsKeycodesBeOS[i].vkCode);
	}
	// numpad keycode vary with numlock
	if (isnumlock)
	{
		for (i = 0; i < length_numlock; i++)
		{
			if (nsKeycodesBeOSNumLock[i].bekeycode == bekeycode)
				return(nsKeycodesBeOSNumLock[i].vkCode);
		}
	}
	else
	{
		for (i = 0; i < length_nonumlock; i++)
		{
			if (nsKeycodesBeOSNoNumLock[i].bekeycode == bekeycode)
				return(nsKeycodesBeOSNoNumLock[i].vkCode);
		}
	}
#ifdef KB_DEBUG
	printf("TranslateBeOSKeyCode: ####### Translation not Found #######\n");
#endif
	return((int)0);
}

//-------------------------------------------------------------------------
//
// OnKeyDown
//
//-------------------------------------------------------------------------
PRBool nsWindow::OnKeyDown(PRUint32 aEventType, const char *bytes,
                           int32 numBytes, PRUint32 mod, PRUint32 bekeycode, int32 rawcode)
{
	PRUint32 aTranslatedKeyCode;
	PRBool noDefault = PR_FALSE;

	mIsShiftDown   = (mod & B_SHIFT_KEY) ? PR_TRUE : PR_FALSE;
	mIsControlDown = (mod & B_CONTROL_KEY) ? PR_TRUE : PR_FALSE;
	mIsAltDown     = ((mod & B_COMMAND_KEY) && !(mod & B_RIGHT_OPTION_KEY))? PR_TRUE : PR_FALSE;
	mIsMetaDown    = (mod & B_LEFT_OPTION_KEY) ? PR_TRUE : PR_FALSE;	
	bool IsNumLocked = ((mod & B_NUM_LOCK) != 0);

	aTranslatedKeyCode = TranslateBeOSKeyCode(bekeycode, IsNumLocked);

	if (numBytes <= 1)
	{
		noDefault  = DispatchKeyEvent(NS_KEY_DOWN, 0, aTranslatedKeyCode);
	}
	else
	{
		//   non ASCII chars
	}

	// ------------  On Char  ------------
	PRUint32	uniChar;

	if ((mIsControlDown || mIsAltDown || mIsMetaDown) && rawcode >= 'a' && rawcode <= 'z')
	{
		if (mIsShiftDown)
			uniChar = rawcode + 'A' - 'a';
		else
			uniChar = rawcode;
		aTranslatedKeyCode = 0;
	} 
	else
	{
		if (numBytes == 0) // deal with unmapped key
			return noDefault;

		switch((unsigned char)bytes[0])
		{
		case 0xc8://System Request
		case 0xca://Break
			return noDefault;// do not send 'KEY_PRESS' message

		case B_INSERT:
		case B_ESCAPE:
		case B_FUNCTION_KEY:
		case B_HOME:
		case B_PAGE_UP:
		case B_END:
		case B_PAGE_DOWN:
		case B_UP_ARROW:
		case B_LEFT_ARROW:
		case B_DOWN_ARROW:
		case B_RIGHT_ARROW:
		case B_TAB:
		case B_DELETE:
		case B_BACKSPACE:
		case B_ENTER:
			uniChar = 0;
			break;

		default:
			// UTF-8 to unicode conversion
			if (numBytes >= 1 && (bytes[0] & 0x80) == 0)
			{
				// 1 byte utf-8 char
				uniChar = bytes[0];
			} 
			else
			{
				if (numBytes >= 2 && (bytes[0] & 0xe0) == 0xc0)
				{
					// 2 byte utf-8 char
					uniChar = ((uint16)(bytes[0] & 0x1f) << 6) | (uint16)(bytes[1] & 0x3f);
				}
				else
				{
					if (numBytes >= 3 && (bytes[0] & 0xf0) == 0xe0)
					{
						// 3 byte utf-8 char
						uniChar = ((uint16)(bytes[0] & 0x0f) << 12) | ((uint16)(bytes[1] & 0x3f) << 6)
						          | (uint16)(bytes[2] & 0x3f);
					}
					else
					{
						//error
						uniChar = 0;
						NS_WARNING("nsWindow::OnKeyDown() error: bytes[] has not enough chars.");
					}
				}
			}
			aTranslatedKeyCode = 0;
			break;
		}
	}

	// If prevent default set for onkeydown, do the same for onkeypress
	PRUint32 extraFlags = (noDefault ? NS_EVENT_FLAG_NO_DEFAULT : 0);
	return DispatchKeyEvent(NS_KEY_PRESS, uniChar, aTranslatedKeyCode, extraFlags) && noDefault;
}

//-------------------------------------------------------------------------
//
// OnKeyUp
//
//-------------------------------------------------------------------------
PRBool nsWindow::OnKeyUp(PRUint32 aEventType, const char *bytes,
                         int32 numBytes, PRUint32 mod, PRUint32 bekeycode, int32 rawcode)
{
	PRUint32 aTranslatedKeyCode;
	bool IsNumLocked = ((mod & B_NUM_LOCK) != 0);

	mIsShiftDown   = (mod & B_SHIFT_KEY) ? PR_TRUE : PR_FALSE;
	mIsControlDown = (mod & B_CONTROL_KEY) ? PR_TRUE : PR_FALSE;
	mIsAltDown     = ((mod & B_COMMAND_KEY) && !(mod & B_RIGHT_OPTION_KEY))? PR_TRUE : PR_FALSE;
	mIsMetaDown    = (mod & B_LEFT_OPTION_KEY) ? PR_TRUE : PR_FALSE;	

	aTranslatedKeyCode = TranslateBeOSKeyCode(bekeycode, IsNumLocked);

	PRBool result = DispatchKeyEvent(NS_KEY_UP, 0, aTranslatedKeyCode);
	return result;

}

//-------------------------------------------------------------------------
//
// DispatchKeyEvent
//
//-------------------------------------------------------------------------

PRBool nsWindow::DispatchKeyEvent(PRUint32 aEventType, PRUint32 aCharCode,
                                  PRUint32 aKeyCode, PRUint32 aFlags)
{
	nsKeyEvent event(PR_TRUE, aEventType, this);
	nsPoint point;

	point.x = 0;
	point.y = 0;

	InitEvent(event, &point); // this add ref's event.widget

	event.flags |= aFlags;
	event.charCode = aCharCode;
	event.keyCode  = aKeyCode;

#ifdef KB_DEBUG
	static int cnt=0;
	printf("%d DispatchKE Type: %s charCode 0x%x  keyCode 0x%x ", cnt++,
	       (NS_KEY_PRESS == aEventType)?"PRESS":(aEventType == NS_KEY_UP?"Up":"Down"),
	       event.charCode, event.keyCode);
	printf("Shift: %s Control %s Alt: %s Meta: %s\n",  
	       (mIsShiftDown?"D":"U"), 
	       (mIsControlDown?"D":"U"), 
	       (mIsAltDown?"D":"U"),
	       (mIsMetaDown?"D":"U"));
#endif

	event.isShift   = mIsShiftDown;
	event.isControl = mIsControlDown;
	event.isMeta   =  mIsMetaDown;
	event.isAlt     = mIsAltDown;

	PRBool result = DispatchWindowEvent(&event);
	NS_RELEASE(event.widget);

	return result;
}

//-------------------------------------------------------------------------
//
// WM_DESTROY has been called
//
//-------------------------------------------------------------------------
void nsWindow::OnDestroy()
{
	mOnDestroyCalled = PR_TRUE;

	// release references to children, device context, toolkit, and app shell
	nsBaseWidget::OnDestroy();

	// dispatch the event
	if (!mIsDestroying) 
	{
		// dispatching of the event may cause the reference count to drop to 0
		// and result in this object being destroyed. To avoid that, add a reference
		// and then release it after dispatching the event
		AddRef();
		DispatchStandardEvent(NS_DESTROY);
		Release();
	}
}

//-------------------------------------------------------------------------
//
// Move
//
//-------------------------------------------------------------------------
PRBool nsWindow::OnMove(PRInt32 aX, PRInt32 aY)
{
	nsGUIEvent event(PR_TRUE, NS_MOVE, this);
	InitEvent(event);
	event.refPoint.x = aX;
	event.refPoint.y = aY;

	PRBool result = DispatchWindowEvent(&event);
	NS_RELEASE(event.widget);
	return result;
}

void nsWindow::OnWheel(PRInt32 aDirection, uint32 aButtons, BPoint aPoint, nscoord aDelta)
{
		// we don't use the mIsXDown bools because
		// they get reset on Gecko reload (makes it harder
		// to use stuff like Alt+Wheel)

		nsMouseScrollEvent scrollEvent(PR_TRUE, NS_MOUSE_SCROLL, this);
		uint32 mod (modifiers());
		scrollEvent.isControl = mod & B_CONTROL_KEY;
		scrollEvent.isShift = mod & B_SHIFT_KEY;
		scrollEvent.isAlt   = mod & B_COMMAND_KEY;
		scrollEvent.isMeta  = mod & B_OPTION_KEY;
						
		scrollEvent.scrollFlags = aDirection;
		scrollEvent.delta = aDelta;
		scrollEvent.time      = PR_IntervalNow();
		scrollEvent.refPoint.x = nscoord(aPoint.x);
		scrollEvent.refPoint.y = nscoord(aPoint.y);

		nsEventStatus rv;
		DispatchEvent (&scrollEvent, rv);
}

//-------------------------------------------------------------------------
//
// Paint
//
//-------------------------------------------------------------------------
nsresult nsWindow::OnPaint(BRegion *breg)
{
	nsresult rv = NS_ERROR_FAILURE;
	if (mView && mView->LockLooper())
	{
		// Substracting area from paintregion
		mView->Validate(breg);
		// looks like it should be done by Mozilla via nsRenderingContext methods,
		// but we saw in some cases how it follows Win32 ideas and don't care about clipping there
		mView->ConstrainClippingRegion(breg);
		mView->UnlockLooper();
	}
	else
		return rv;
	BRect br = breg->Frame();
	if (!br.IsValid() || !mEventCallback || !mView  || (eWindowType_child != mWindowType && eWindowType_popup != mWindowType))
		return rv;
	nsRect nsr(nscoord(br.left), nscoord(br.top), 
			nscoord(br.IntegerWidth() + 1), nscoord(br.IntegerHeight() + 1));
	mUpdateArea->SetTo(0,0,0,0);
	int numrects = breg->CountRects();
	for (int i = 0; i< numrects; i++)
	{
		BRect br = breg->RectAt(i);
		mUpdateArea->Union(int(br.left), int(br.top), 
							br.IntegerWidth() + 1, br.IntegerHeight() + 1);
	}	

	nsIRenderingContext* rc = GetRenderingContext();
	if (NS_UNLIKELY(!rc)) {
		return NS_ERROR_FAILURE;
	}

	// Double buffering for cairo builds is done here
	nsRefPtr<gfxContext> ctx = rc->ThebesContext();
	ctx->Save();

	// Clip
	ctx->NewPath();
	for (int i = 0; i< numrects; i++)
	{
		BRect br = breg->RectAt(i);
		ctx->Rectangle(gfxRect(int(br.left), int(br.top), 
			       br.IntegerWidth() + 1, br.IntegerHeight() + 1));
	}
	ctx->Clip();

	// double buffer
	ctx->PushGroup(gfxContext::CONTENT_COLOR);

	nsPaintEvent event(PR_TRUE, NS_PAINT, this);

	InitEvent(event);
	event.region = mUpdateArea;
	event.rect = &nsr;
	event.renderingContext = rc;
	if (event.renderingContext != nsnull)
	{
		// TODO: supply nsRenderingContextBeOS with font, colors and other state variables here.
		// It will help toget rid of some hacks in LockAndUpdateView and
		// allow non-permanent nsDrawingSurface for BeOS - currently it fails for non-bitmapped BViews/widgets.
		// Something like this:
		//if (mFontMetrics)
		//	event.renderingContext->SetFont(mFontMetrics);
		rv = DispatchWindowEvent(&event) ? NS_OK : NS_ERROR_FAILURE;
		NS_RELEASE(event.renderingContext);
	}

	NS_RELEASE(event.widget);

	// The second half of double buffering
	if (rv == NS_OK) {
		ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
		ctx->PopGroupToSource();
		ctx->Paint();
	} else {
		// ignore
		ctx->PopGroup();
	}

	ctx->Restore();

	return rv;
}


//-------------------------------------------------------------------------
//
// Send a resize message to the listener
//
//-------------------------------------------------------------------------
PRBool nsWindow::OnResize(nsRect &aWindowRect)
{
	// call the event callback
	if (mEventCallback)
	{
		nsSizeEvent event(PR_TRUE, NS_SIZE, this);
		InitEvent(event);
		event.windowSize = &aWindowRect;
		// We have same size for windows rect and "client area" rect
		event.mWinWidth  = aWindowRect.width;
		event.mWinHeight = aWindowRect.height;
		PRBool result = DispatchWindowEvent(&event);
		NS_RELEASE(event.widget);
		return result;
	}
	return PR_FALSE;
}



//-------------------------------------------------------------------------
//
// Deal with all sort of mouse event
//
//-------------------------------------------------------------------------
PRBool nsWindow::DispatchMouseEvent(PRUint32 aEventType, nsPoint aPoint, PRUint32 clicks, PRUint32 mod,
                                    PRUint16 aButton)
{
	PRBool result = PR_FALSE;
	if (nsnull != mEventCallback)
	{
		nsMouseEvent event(PR_TRUE, aEventType, this, nsMouseEvent::eReal);
		InitEvent (event, &aPoint);
		event.isShift   = mod & B_SHIFT_KEY;
		event.isControl = mod & B_CONTROL_KEY;
		event.isAlt     = mod & B_COMMAND_KEY;
		event.isMeta     = mod & B_OPTION_KEY;
		event.clickCount = clicks;
		event.button = aButton;

		// call the event callback
    result = DispatchWindowEvent(&event);
    NS_RELEASE(event.widget);
    return result;
	}

	return PR_FALSE;
}

//-------------------------------------------------------------------------
//
// Deal with focus messages
//
//-------------------------------------------------------------------------
PRBool nsWindow::DispatchFocus(PRUint32 aEventType)
{
	// call the event callback
	if (mEventCallback)
		return(DispatchStandardEvent(aEventType));

	return PR_FALSE;
}

NS_METHOD nsWindow::SetTitle(const nsAString& aTitle)
{
	if (mView && mView->LockLooper())
	{
		mView->Window()->SetTitle(NS_ConvertUTF16toUTF8(aTitle).get());
		mView->UnlockLooper();
	}
	return NS_OK;
}

//----------------------------------------------------
//
// Get/Set the preferred size
//
//----------------------------------------------------
NS_METHOD nsWindow::GetPreferredSize(PRInt32& aWidth, PRInt32& aHeight)
{
	// TODO:  Check to see how often this is called.  If too much, leave as is,
	// otherwise, call mView->GetPreferredSize
	aWidth  = mPreferredWidth;
	aHeight = mPreferredHeight;
	return NS_ERROR_FAILURE;
}

NS_METHOD nsWindow::SetPreferredSize(PRInt32 aWidth, PRInt32 aHeight)
{
	mPreferredWidth  = aWidth;
	mPreferredHeight = aHeight;
	return NS_OK;
}

//----------------------------------------------------
// Special Sub-Class
//----------------------------------------------------
nsIWidgetStore::nsIWidgetStore( nsIWidget *aWidget )
		: mWidget( aWidget )
{
	// NS_ADDREF/NS_RELEASE is not needed here.
	// This class is used as internal (BeOS native) object of nsWindow,
	// so it must not addref/release nsWindow here.
	// Otherwise, nsWindow object will leak. (Makoto Hamanaka)
}

nsIWidgetStore::~nsIWidgetStore()
{
}

nsIWidget *nsIWidgetStore::GetMozillaWidget(void)
{
	return mWidget;
}

//----------------------------------------------------
// BeOS Sub-Class Window
//----------------------------------------------------

nsWindowBeOS::nsWindowBeOS( nsIWidget *aWidgetWindow, BRect aFrame, const char *aName, window_look aLook,
                            window_feel aFeel, int32 aFlags, int32 aWorkspace )
		: BWindow( aFrame, aName, aLook, aFeel, aFlags, aWorkspace ),
		nsIWidgetStore( aWidgetWindow )
{
	fJustGotBounds = true;
}

nsWindowBeOS::~nsWindowBeOS()
{
	//placeholder for clean up
}

bool nsWindowBeOS::QuitRequested( void )
{
	if (CountChildren() != 0)
	{
		nsWindow	*w = (nsWindow *)GetMozillaWidget();
		nsToolkit	*t;
		if (w && (t = w->GetToolkit()) != 0)
		{
			MethodInfo *info = nsnull;
			if (nsnull != (info = new MethodInfo(w, w, nsSwitchToUIThread::CLOSEWINDOW)))
				t->CallMethodAsync(info);
		}
	}
	return true;
}

void nsWindowBeOS::MessageReceived(BMessage *msg)
{
	// Temp replacement for real DnD. Supports file drop onto window.
	if (msg->what == B_SIMPLE_DATA)
	{
		printf("BWindow::SIMPLE_DATA\n");
		be_app_messenger.SendMessage(msg);
	}
	BWindow::MessageReceived(msg);
}

// This function calls KeyDown() for Alt+whatever instead of app_server,
// also used for Destroy workflow 
void nsWindowBeOS::DispatchMessage(BMessage *msg, BHandler *handler)
{
	if (msg->what == B_KEY_DOWN && modifiers() & B_COMMAND_KEY)
	{
		BString bytes;
		if (B_OK == msg->FindString("bytes", &bytes))
		{
			BView *view = this->CurrentFocus();
			if (view)
				view->KeyDown(bytes.String(), bytes.Length());
		}
		if (strcmp(bytes.String(),"w") && strcmp(bytes.String(),"W"))
			BWindow::DispatchMessage(msg, handler);
	}
	// In some cases the message don't reach QuitRequested() hook,
	// so do it here
	else if(msg->what == B_QUIT_REQUESTED)
	{
		// tells nsWindow to kill me
		nsWindow	*w = (nsWindow *)GetMozillaWidget();
		nsToolkit	*t;
		if (w && (t = w->GetToolkit()) != 0)
		{
			MethodInfo *info = nsnull;
			if (nsnull != (info = new MethodInfo(w, w, nsSwitchToUIThread::CLOSEWINDOW)))
				t->CallMethodAsync(info);
		}		
	}
	else
		BWindow::DispatchMessage(msg, handler);
}

//This method serves single purpose here - allows Mozilla to save current window position,
//and restore position on new start. 
void nsWindowBeOS::FrameMoved(BPoint origin)
{	

	//determine if the window position actually changed
	if (origin.x == lastWindowPoint.x && origin.x == lastWindowPoint.x) 
	{
		//it didn't - don't bother
		return;
	}
	lastWindowPoint = origin;
	nsWindow  *w = (nsWindow *)GetMozillaWidget();
	nsToolkit *t;
	if (w && (t = w->GetToolkit()) != 0) 
	{
		MethodInfo *info = nsnull;
		if (nsnull != (info = new MethodInfo(w, w, nsSwitchToUIThread::ONMOVE)))
			t->CallMethodAsync(info);
	}
}

void nsWindowBeOS::WindowActivated(bool active)
{
// Calls method ONACTIVATE to dispatch focus ACTIVATE messages
	nsWindow        *w = (nsWindow *)GetMozillaWidget();
	nsToolkit	*t;
	if (w && (t = w->GetToolkit()) != 0)
	{
		uint32	args[2];
		args[0] = (uint32)active;
		args[1] = (uint32)this;
		MethodInfo *info = nsnull;
		if (nsnull != (info = new MethodInfo(w, w, nsSwitchToUIThread::ONACTIVATE, 2, args)))
			t->CallMethodAsync(info);
	}
}

void  nsWindowBeOS::WorkspacesChanged(uint32 oldworkspace, uint32 newworkspace)
{
	if (oldworkspace == newworkspace)
		return;
	nsWindow        *w = (nsWindow *)GetMozillaWidget();
	nsToolkit	*t;
	if (w && (t = w->GetToolkit()) != 0)
	{
		uint32	args[2];
		args[0] = newworkspace;
		args[1] = oldworkspace;
		MethodInfo *info = nsnull;
		if (nsnull != (info = new MethodInfo(w, w, nsSwitchToUIThread::ONWORKSPACE, 2, args)))
			t->CallMethodAsync(info);
	}	
}

void  nsWindowBeOS::FrameResized(float width, float height)
{
	// We have send message already, and Mozilla still didn't get it
	// so don't poke it endlessly with no reason
	if (!fJustGotBounds)
		return;
	nsWindow        *w = (nsWindow *)GetMozillaWidget();
	nsToolkit	*t;
	if (w && (t = w->GetToolkit()) != 0)
	{
		MethodInfo *info = nsnull;
		if (nsnull != (info = new MethodInfo(w, w, nsSwitchToUIThread::ONRESIZE)))
		{
			//Memorize fact of sending message
			if (t->CallMethodAsync(info))
				fJustGotBounds = false;
		}
	}	
}

//----------------------------------------------------
// BeOS Sub-Class View
//----------------------------------------------------

nsViewBeOS::nsViewBeOS(nsIWidget *aWidgetWindow, BRect aFrame, const char *aName, uint32 aResizingMode, uint32 aFlags)
	: BView(aFrame, aName, aResizingMode, aFlags), nsIWidgetStore(aWidgetWindow), wheel(.0,.0)
{
	SetViewColor(B_TRANSPARENT_COLOR);
	paintregion.MakeEmpty();	
	buttons = 0;
	fRestoreMouseMask = false;
	fJustValidated = true;
	fWheelDispatched = true;
	fVisible = true;
}

void nsViewBeOS::SetVisible(bool visible)
{
	if (visible)
		SetFlags(Flags() | B_WILL_DRAW);
	else
		SetFlags(Flags() & ~B_WILL_DRAW);
	fVisible = visible;
}

inline bool nsViewBeOS::Visible()
{
	return fVisible;
}
 
void nsViewBeOS::Draw(BRect updateRect)
{
	// Ignore all, we are scrolling.
	if (!fVisible)
		return;

	paintregion.Include(updateRect);

	// We have send message already, and Mozilla still didn't get it
	// so don't poke it endlessly with no reason. Also don't send message
	// if update region is empty.
	if (paintregion.CountRects() == 0 || !paintregion.Frame().IsValid() || !fJustValidated)
		return;
	uint32	args[1];
	args[0] = (uint32)this;
	nsWindow	*w = (nsWindow *)GetMozillaWidget();
	nsToolkit	*t;
	if (w && (t = w->GetToolkit()) != 0)
	{
		MethodInfo *info = nsnull;
		info = new MethodInfo(w, w, nsSwitchToUIThread::ONPAINT, 1, args);
		if (info)
		{
			//Memorize fact of sending message
			if (t->CallMethodAsync(info))
				fJustValidated = false;
		}
	}
}

// Method to get update rects for asynchronous drawing.
bool nsViewBeOS::GetPaintRegion(BRegion *r)
{

	// Mozilla got previous ONPAINT message,
	// ready for next event.
	fJustValidated = true;
	if (paintregion.CountRects() == 0)
		return false;
	r->Include(&paintregion);
	return true;
}

// Method to remove painted rects from pending update region
void nsViewBeOS::Validate(BRegion *reg)
{
	paintregion.Exclude(reg);
}

BPoint nsViewBeOS::GetWheel()
{
	BPoint retvalue = wheel;
	// Mozilla got wheel event, so setting flag and cleaning delta storage
	fWheelDispatched = true;
	wheel.x = 0;
	wheel.y = 0;
	return retvalue;
}

void nsViewBeOS::MouseDown(BPoint point)
{
	if (!fRestoreMouseMask)
		mouseMask = SetMouseEventMask(B_POINTER_EVENTS);
	fRestoreMouseMask = true;
	
	//To avoid generating extra mouseevents when there is no change in pos.
	mousePos = point;

	uint32 clicks = 0;
	BMessage *msg = Window()->CurrentMessage();
	msg->FindInt32("buttons", (int32 *) &buttons);
	msg->FindInt32("clicks", (int32 *) &clicks);

	if (0 == buttons)
		return;

	nsWindow	*w = (nsWindow *) GetMozillaWidget();
	if (w == NULL)
		return;
		
	nsToolkit	*t = w->GetToolkit();
	if (t == NULL)
		return;

	PRUint16 eventButton =
	  (buttons & B_PRIMARY_MOUSE_BUTTON) ? nsMouseEvent::eLeftButton :
	    ((buttons & B_SECONDARY_MOUSE_BUTTON) ? nsMouseEvent::eRightButton :
	      nsMouseEvent::eMiddleButton);
	uint32	args[6];
	args[0] = NS_MOUSE_BUTTON_DOWN;
	args[1] = (uint32) point.x;
	args[2] = (uint32) point.y;
	args[3] = clicks;
	args[4] = modifiers();
	args[5] = eventButton;
	MethodInfo *info = nsnull;
	if (nsnull != (info = new MethodInfo(w, w, nsSwitchToUIThread::BTNCLICK, 6, args)))
		t->CallMethodAsync(info);
}

void nsViewBeOS::MouseMoved(BPoint point, uint32 transit, const BMessage *msg)
{
	//To avoid generating extra mouseevents when there is no change in pos.
	//and not entering exiting view.
	if (mousePos == point && (transit == B_INSIDE_VIEW || transit == B_OUTSIDE_VIEW))
		return;

	mousePos = point;
		
	//We didn't start the mouse down and there is no drag in progress, so ignore.
	if (NULL == msg && !fRestoreMouseMask && buttons)
		return;
		
	nsWindow	*w = (nsWindow *)GetMozillaWidget();
	if (w == NULL)
		return;
	nsToolkit	*t = w->GetToolkit();
	if (t == NULL)
		return;
	uint32	args[4];
	args[1] = (int32) point.x;
	args[2] = (int32) point.y;
	args[3] = modifiers();

	switch (transit)
 	{
 	case B_ENTERED_VIEW:
		{
			args[0] = NULL != msg ? NS_DRAGDROP_ENTER : NS_MOUSE_ENTER;
			if (msg == NULL)
				break;
			nsCOMPtr<nsIDragService> dragService = do_GetService(kCDragServiceCID);
			dragService->StartDragSession();
			//As it may have come from the outside we need to update this.
			nsCOMPtr<nsIDragSessionBeOS> dragSessionBeOS = do_QueryInterface(dragService);
			dragSessionBeOS->UpdateDragMessageIfNeeded(new BMessage(*msg));
		}
		break;
	case B_EXITED_VIEW:
		{
			args[0] = NULL != msg ? NS_DRAGDROP_EXIT : NS_MOUSE_EXIT;
			if (msg == NULL)
				break;
			nsCOMPtr<nsIDragService> dragService = do_GetService(kCDragServiceCID);
			dragService->EndDragSession(PR_FALSE);
		}
		break;
	default:
		args[0]= msg == NULL ? NS_MOUSE_MOVE : NS_DRAGDROP_OVER;
        // fire the drag event at the source
        if (msg != NULL) {
			nsCOMPtr<nsIDragService> dragService = do_GetService(kCDragServiceCID);
			dragService->FireDragEventAtSource(NS_DRAGDROP_DRAG);
        }
 	}
 	
	MethodInfo *moveInfo = nsnull;
	if (nsnull != (moveInfo = new MethodInfo(w, w, nsSwitchToUIThread::ONMOUSE, 4, args)))
		t->CallMethodAsync(moveInfo);
}

void nsViewBeOS::MouseUp(BPoint point)
{
	if (fRestoreMouseMask) 
	{
		SetMouseEventMask(mouseMask);
		fRestoreMouseMask = false;
	}
	
	//To avoid generating extra mouseevents when there is no change in pos.
	mousePos = point;

	PRUint16 eventButton =
	  (buttons & B_PRIMARY_MOUSE_BUTTON) ? nsMouseEvent::eLeftButton :
	    ((buttons & B_SECONDARY_MOUSE_BUTTON) ? nsMouseEvent::eRightButton :
	      nsMouseEvent::eMiddleButton);
	
	nsWindow	*w = (nsWindow *)GetMozillaWidget();
	if (w == NULL)
		return;
	nsToolkit	*t = w->GetToolkit();
	if (t == NULL)
		return;


	uint32	args[6];
	args[0] = NS_MOUSE_BUTTON_UP;
	args[1] = (uint32) point.x;
	args[2] = (int32) point.y;
	args[3] = 0;
	args[4] = modifiers();
	args[5] = eventButton;
	MethodInfo *info = nsnull;
	if (nsnull != (info = new MethodInfo(w, w, nsSwitchToUIThread::BTNCLICK, 6, args)))
		t->CallMethodAsync(info);
}

void nsViewBeOS::MessageReceived(BMessage *msg)
{
	if(msg->WasDropped())
	{
		nsWindow	*w = (nsWindow *)GetMozillaWidget();
		if (w == NULL)
			return;
		nsToolkit	*t = w->GetToolkit();
		if (t == NULL)
			return;

		uint32	args[4];
		args[0] = NS_DRAGDROP_DROP;

		//Drop point is in screen-cordinates
		BPoint aPoint = ConvertFromScreen(msg->DropPoint()); 
	
		args[1] = (uint32) aPoint.x;
		args[2] = (uint32) aPoint.y;
		args[3] = modifiers();

		MethodInfo *info = new MethodInfo(w, w, nsSwitchToUIThread::ONDROP, 4, args);
		t->CallMethodAsync(info);
		BView::MessageReceived(msg);
		return;
	}

	switch(msg->what)
	{
	//Native drag'n'drop negotiation
	case B_COPY_TARGET:
	case B_MOVE_TARGET:
	case B_LINK_TARGET:
	case B_TRASH_TARGET:
		{
			nsCOMPtr<nsIDragService> dragService = do_GetService(kCDragServiceCID);
			nsCOMPtr<nsIDragSessionBeOS> dragSessionBeOS = do_QueryInterface(dragService);
			dragSessionBeOS->TransmitData(new BMessage(*msg));
		}
		break;
	case B_UNMAPPED_KEY_DOWN:
		//printf("unmapped_key_down\n");
		KeyDown(NULL, 0);
		break;

	case B_UNMAPPED_KEY_UP:
		//printf("unmapped_key_up\n");
		KeyUp(NULL, 0);
		break;

	case B_MOUSE_WHEEL_CHANGED:
		{
			float wheel_y;
			float wheel_x;

			msg->FindFloat ("be:wheel_delta_y", &wheel_y);
			msg->FindFloat ("be:wheel_delta_x", &wheel_x);
			wheel.x += wheel_x;
			wheel.y += wheel_y;

			if(!fWheelDispatched || (nscoord(wheel_x) == 0 && nscoord(wheel_y) == 0))
				return;
			uint32	args[1];
			args[0] = (uint32)this;
			nsWindow    *w = (nsWindow *)GetMozillaWidget();
			nsToolkit   *t;

			if (w && (t = w->GetToolkit()) != 0)
			{
					
				MethodInfo *info = nsnull;
				if (nsnull != (info = new MethodInfo(w, w, nsSwitchToUIThread::ONWHEEL, 1, args)))
				{
					if (t->CallMethodAsync(info))
						fWheelDispatched = false;
					
				}
			}
		}
		break;
		
#if defined(BeIME)
	case B_INPUT_METHOD_EVENT:
		DoIME(msg);
		break;
#endif
	default :
		BView::MessageReceived(msg);
		break;
	}
}

void nsViewBeOS::KeyDown(const char *bytes, int32 numBytes)
{
	nsWindow	*w = (nsWindow *)GetMozillaWidget();
	nsToolkit	*t;
	int32 keycode = 0;
	int32 rawcode = 0;

	BMessage *msg = this->Window()->CurrentMessage();
	if (msg)
	{
		msg->FindInt32("key", &keycode);
		msg->FindInt32("raw_char", &rawcode);
	}

	if (w && (t = w->GetToolkit()) != 0)
	{
		uint32 bytebuf = 0;
		uint8 *byteptr = (uint8 *)&bytebuf;
		for(int32 i = 0; i < numBytes; i++)
			byteptr[i] = bytes[i];

		uint32	args[6];
		args[0] = NS_KEY_DOWN;
		args[1] = bytebuf;
		args[2] = numBytes;
		args[3] = modifiers();
		args[4] = keycode;
		args[5] = rawcode;
		MethodInfo *info = nsnull;
		if (nsnull != (info = new MethodInfo(w, w, nsSwitchToUIThread::ONKEY, 6, args)))
			t->CallMethodAsync(info);
	}
}

void nsViewBeOS::KeyUp(const char *bytes, int32 numBytes)
{
	nsWindow	*w = (nsWindow *)GetMozillaWidget();
	nsToolkit	*t;
	int32 keycode = 0;
	int32 rawcode = 0;
	BMessage *msg = this->Window()->CurrentMessage();
	if (msg)
	{
		msg->FindInt32("key", &keycode);
		msg->FindInt32("raw_char", &rawcode);
	}

	if (w && (t = w->GetToolkit()) != 0)
	{
		uint32 bytebuf = 0;
		uint8 *byteptr = (uint8 *)&bytebuf;
		for(int32 i = 0; i < numBytes; i++)
			byteptr[i] = bytes[i];

		uint32	args[6];
		args[0] = NS_KEY_UP;
		args[1] = (int32)bytebuf;
		args[2] = numBytes;
		args[3] = modifiers();
		args[4] = keycode;
		args[5] = rawcode;
		MethodInfo *info = nsnull;
		if (nsnull != (info = new MethodInfo(w, w, nsSwitchToUIThread::ONKEY, 6, args)))
			t->CallMethodAsync(info);
	}
}

void nsViewBeOS::MakeFocus(bool focused)
{
	if (!IsFocus() && focused)
		BView::MakeFocus(focused);
	uint32	args[1];
	args[0] = (uint32)this;
	nsWindow	*w = (nsWindow *)GetMozillaWidget();
	nsToolkit	*t;
	if (w && (t = w->GetToolkit()) != 0)
	{
		MethodInfo *info = nsnull;
		if (!focused)
		{
			if (nsnull != (info = new MethodInfo(w, w, nsSwitchToUIThread::KILL_FOCUS, 1, args)))
				t->CallMethodAsync(info);
		}
#ifdef DEBUG_FOCUS
		else
		{
			if (nsnull != (info = new MethodInfo(w, w, nsSwitchToUIThread::GOT_FOCUS, 1, args)))
				t->CallMethodAsync(info);
		}
#endif		
	}
}

#if defined(BeIME)
// Inline Input Method implementation
void nsViewBeOS::DoIME(BMessage *msg)
{
	nsWindow	*w = (nsWindow *)GetMozillaWidget();
	nsToolkit	*t;

	if(w && (t = w->GetToolkit()) != 0) 
	{
		ssize_t size = msg->FlattenedSize();
		int32		argc = (size+3)/4;
		uint32 *args = new uint32[argc];
		if (args) 
		{
			msg->Flatten((char*)args, size);
			MethodInfo *info = new MethodInfo(w, w, nsSwitchToUIThread::ONIME, argc, args);
			if (info) 
			{
				t->CallMethodAsync(info);
				NS_RELEASE(t);
			}
			delete[] args;
		}	
	}
}
#endif
