/* -*- 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 * Sergei Dolgov * Fredrik Holmqvist * Mats Palmgren * * 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 #include #include #include #include #include #include #include #if defined(BeIME) #include #include #include #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; 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 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 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 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 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 menuRollup ( do_QueryInterface(gRollupListener) ); if ( menuRollup ) { nsAutoTArray 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(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 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(®); 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(®); 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(®); 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(®); mView->UnlockLooper(); // Look if native update calls above filled update region and paint it if (nonempty) rv = OnPaint(®); } 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(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(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(®); mView->UnlockLooper(); if (nonempty) OnPaint(®); } } 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 dragService = do_GetService(kCDragServiceCID); if (dragService) { nsCOMPtr 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 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 dragService = do_GetService(kCDragServiceCID); dragService->StartDragSession(); //As it may have come from the outside we need to update this. nsCOMPtr 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 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 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 dragService = do_GetService(kCDragServiceCID); nsCOMPtr 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