/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape 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/NPL/ * * 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 Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): */ // ***IMPORTANT*** // On all platforms, we are assuming in places that the implementation of |nsIWidget| // is really |nsWindow| and then calling methods specific to nsWindow to finish the job. // This is by design and the assumption is safe because an nsIWidget can only be created through // our Widget factory where all our widgets, including the XP widgets, inherit from nsWindow. // Still, in the places (or most of them) where this assumption is done, a |static_cast| has been used. // A similar warning is in nsWidgetFactory.cpp. #include "nsWindow.h" #include "nsIFontMetrics.h" #include "nsIDeviceContext.h" #include "nsCOMPtr.h" #include "nsToolkit.h" #include "nsIEnumerator.h" #include "prmem.h" #include #include #include #include #include "nsplugindefs.h" #include "nsMacEventHandler.h" #include "nsMacResources.h" #include "nsRegionMac.h" #include "nsIRollupListener.h" #include "nsCarbonHelpers.h" #include "nsGfxUtils.h" #if PINK_PROFILING #include "profilerutils.h" #endif //////////////////////////////////////////////////// nsIRollupListener * gRollupListener = nsnull; nsIWidget * gRollupWidget = nsnull; // Since we only want a single notification pending for the app we'll declare // these static static NMRec gNMRec; static Boolean gNotificationInstalled = false; // Routines for iterating over the rects of a region. Carbon and pre-Carbon // do this differently so provide a way to do both. #if TARGET_CARBON static RegionToRectsUPP sUpdateRectProc = nsnull; static RegionToRectsUPP sAddRectToArrayProc = nsnull; static RegionToRectsUPP sCountRectProc = nsnull; #else void EachRegionRect (RgnHandle r, void (* proc)(Rect *, void *), void* data) ; #endif #pragma mark - //#define PAINT_DEBUGGING //#define BLINK_DEBUGGING #if TARGET_CARBON #undef PAINT_DEBUGGING #undef BLINK_DEBUGGING #endif #ifdef BLINK_DEBUGGING static void blinkRect(Rect* r); static void blinkRgn(RgnHandle rgn); #endif //------------------------------------------------------------------------- // // nsWindow constructor // //------------------------------------------------------------------------- nsWindow::nsWindow() : nsBaseWidget() , nsDeleteObserved(this), nsIKBStateControl() { WIDGET_SET_CLASSNAME("nsWindow"); mParent = nsnull; mBounds.SetRect(0,0,0,0); mResizingChildren = PR_FALSE; mVisible = PR_FALSE; mEnabled = PR_TRUE; SetPreferredSize(0,0); mFontMetrics = nsnull; mMenuBar = nsnull; mTempRenderingContext = nsnull; mWindowRegion = nsnull; mVisRegion = nsnull; mWindowPtr = nsnull; mDrawing = PR_FALSE; mDestroyCalled = PR_FALSE; mDestructorCalled = PR_FALSE; SetBackgroundColor(NS_RGB(255, 255, 255)); SetForegroundColor(NS_RGB(0, 0, 0)); mPluginPort = nsnull; AcceptFocusOnClick(PR_TRUE); #if TARGET_CARBON if ( !sUpdateRectProc ) { sUpdateRectProc = NewRegionToRectsUPP ( PaintUpdateRectProc ); sAddRectToArrayProc = NewRegionToRectsUPP ( AddRectToArrayProc ); sCountRectProc = NewRegionToRectsUPP ( CountRectProc ); } #endif } //------------------------------------------------------------------------- // // nsWindow destructor // //------------------------------------------------------------------------- nsWindow::~nsWindow() { // notify the children that we're gone nsCOMPtr children ( getter_AddRefs(GetChildren()) ); if (children) { children->First(); do { nsISupports* child; if (NS_SUCCEEDED(children->CurrentItem(&child))) { nsWindow* childWindow = static_cast(static_cast(child)); NS_RELEASE(child); childWindow->mParent = nsnull; } } while (NS_SUCCEEDED(children->Next())); } mDestructorCalled = PR_TRUE; //Destroy(); if (mWindowRegion) { ::DisposeRgn(mWindowRegion); mWindowRegion = nsnull; } if (mVisRegion) { ::DisposeRgn(mVisRegion); mVisRegion = nsnull; } NS_IF_RELEASE(mTempRenderingContext); NS_IF_RELEASE(mFontMetrics); NS_IF_RELEASE(mMenuBar); NS_IF_RELEASE(mMenuListener); if (mPluginPort) { delete mPluginPort; } } NS_IMPL_ISUPPORTS_INHERITED1(nsWindow, nsBaseWidget, nsIKBStateControl); //------------------------------------------------------------------------- // // Utility method for implementing both Create(nsIWidget ...) and // Create(nsNativeWidget...) //------------------------------------------------------------------------- nsresult nsWindow::StandardCreate(nsIWidget *aParent, const nsRect &aRect, EVENT_CALLBACK aHandleEventFunction, nsIDeviceContext *aContext, nsIAppShell *aAppShell, nsIToolkit *aToolkit, nsWidgetInitData *aInitData, nsNativeWidget aNativeParent) // should always be nil here { mParent = aParent; mBounds = aRect; CalcWindowRegions(); BaseCreate(aParent, aRect, aHandleEventFunction, aContext, aAppShell, aToolkit, aInitData); if (mParent) { SetBackgroundColor(mParent->GetBackgroundColor()); SetForegroundColor(mParent->GetForegroundColor()); } if (mWindowPtr == nsnull) { if (aParent) mWindowPtr = (WindowPtr)aParent->GetNativeData(NS_NATIVE_DISPLAY); /* this is always null else if (aAppShell) mWindowPtr = (WindowPtr)aAppShell->GetNativeData(NS_NATIVE_SHELL); */ } return NS_OK; } //------------------------------------------------------------------------- // // create a nswindow // //------------------------------------------------------------------------- NS_IMETHODIMP nsWindow::Create(nsIWidget *aParent, const nsRect &aRect, EVENT_CALLBACK aHandleEventFunction, nsIDeviceContext *aContext, nsIAppShell *aAppShell, nsIToolkit *aToolkit, nsWidgetInitData *aInitData) { return(StandardCreate(aParent, aRect, aHandleEventFunction, aContext, aAppShell, aToolkit, aInitData, nsnull)); } //------------------------------------------------------------------------- // // Creates a main nsWindow using a native widget // //------------------------------------------------------------------------- NS_IMETHODIMP nsWindow::Create(nsNativeWidget aNativeParent, // this is a nsWindow* const nsRect &aRect, EVENT_CALLBACK aHandleEventFunction, nsIDeviceContext *aContext, nsIAppShell *aAppShell, nsIToolkit *aToolkit, nsWidgetInitData *aInitData) { // On Mac, a native widget is a nsWindow* because // nsWindow::GetNativeData(NS_NATIVE_WIDGET) returns 'this' nsIWidget* aParent = (nsIWidget*)aNativeParent; return(Create(aParent, aRect, aHandleEventFunction, aContext, aAppShell, aToolkit, aInitData)); } //------------------------------------------------------------------------- // // Close this nsWindow // //------------------------------------------------------------------------- NS_IMETHODIMP nsWindow::Destroy() { if (mDestroyCalled) return NS_OK; mDestroyCalled = PR_TRUE; nsBaseWidget::OnDestroy(); nsBaseWidget::Destroy(); NS_IF_RELEASE(mMenuBar); SetMenuBar(nsnull); ReportDestroyEvent(); // beard: this seems to cause the window to be deleted. moved all release code to destructor. return NS_OK; } #pragma mark - //------------------------------------------------------------------------- // // Get this nsWindow parent // //------------------------------------------------------------------------- nsIWidget* nsWindow::GetParent(void) { NS_IF_ADDREF(mParent); return mParent; } //------------------------------------------------------------------------- // // Return some native data according to aDataType // //------------------------------------------------------------------------- void* nsWindow::GetNativeData(PRUint32 aDataType) { nsPoint point; void* retVal = nsnull; switch (aDataType) { case NS_NATIVE_WIDGET: case NS_NATIVE_WINDOW: retVal = (void*)this; break; case NS_NATIVE_GRAPHIC: // pinkerton // Windows and GrafPorts are VERY different under Carbon, and we can no // longer pass them interchagably. When we ask for a GrafPort, we cannot // return a window or vice versa. retVal = (void*)::GetWindowPort(mWindowPtr); break; case NS_NATIVE_DISPLAY: retVal = (void*)mWindowPtr; break; case NS_NATIVE_REGION: retVal = (void*)mVisRegion; break; case NS_NATIVE_COLORMAP: //¥TODO break; case NS_NATIVE_OFFSETX: point.MoveTo(mBounds.x, mBounds.y); LocalToWindowCoordinate(point); retVal = (void*)point.x; break; case NS_NATIVE_OFFSETY: point.MoveTo(mBounds.x, mBounds.y); LocalToWindowCoordinate(point); retVal = (void*)point.y; break; case NS_NATIVE_PLUGIN_PORT: // this needs to be a combination of the port and the offsets. if (mPluginPort == nsnull) mPluginPort = new nsPluginPort; point.MoveTo(mBounds.x, mBounds.y); LocalToWindowCoordinate(point); // for compatibility with 4.X, this origin is what you'd pass // to SetOrigin. mPluginPort->port = ::GetWindowPort(mWindowPtr); mPluginPort->portx = -point.x; mPluginPort->porty = -point.y; retVal = (void*)mPluginPort; } return retVal; } #pragma mark - //------------------------------------------------------------------------- // // Return PR_TRUE if the whether the component is visible, PR_FALSE otherwise // //------------------------------------------------------------------------- NS_METHOD nsWindow::IsVisible(PRBool & bState) { bState = mVisible; return NS_OK; } //------------------------------------------------------------------------- // // Hide or show this component // //------------------------------------------------------------------------- NS_IMETHODIMP nsWindow::Show(PRBool bState) { mVisible = bState; return NS_OK; } NS_IMETHODIMP nsWindow::ModalEventFilter(PRBool aRealEvent, void *aEvent, PRBool *aForWindow) { *aForWindow = PR_FALSE; EventRecord *theEvent = (EventRecord *) aEvent; if (aRealEvent && theEvent->what != nullEvent ) { WindowPtr window = (WindowPtr) GetNativeData(NS_NATIVE_DISPLAY); WindowPtr rollupWindow = gRollupWidget ? (WindowPtr) gRollupWidget->GetNativeData(NS_NATIVE_DISPLAY) : nsnull; WindowPtr eventWindow = nsnull; PRInt16 where = ::FindWindow ( theEvent->where, &eventWindow ); PRBool inWindow = eventWindow && (eventWindow == window || eventWindow == rollupWindow); switch ( theEvent->what ) { // is it a mouse event? case mouseUp: *aForWindow = PR_TRUE; break; case mouseDown: // is it in the given window? // (note we also let some events questionable for modal dialogs pass through. // but it makes sense that the draggability et.al. of a modal window should // be controlled by whether the window has a drag bar). if (inWindow) { if ( where == inContent || where == inDrag || where == inGrow || where == inGoAway || where == inZoomIn || where == inZoomOut ) *aForWindow = PR_TRUE; } else // we're in another window. { // let's handle dragging of background windows here if (eventWindow && (where == inDrag) && (theEvent->modifiers & cmdKey)) { Rect screenRect; ::GetRegionBounds(::GetGrayRgn(), &screenRect); ::DragWindow(eventWindow, theEvent->where, &screenRect); } *aForWindow = PR_FALSE; } break; case keyDown: case keyUp: case autoKey: *aForWindow = PR_TRUE; break; case diskEvt: // I think dialogs might want to support floppy insertion, and it doesn't // interfere with modality... case updateEvt: // always let update events through, because if we don't handle them, we're // doomed! case activateEvt: // activate events aren't so much a request as simply informative. might // as well acknowledge them. *aForWindow = PR_TRUE; break; case osEvt: // check for mouseMoved or suspend/resume events. We especially need to // let suspend/resume events through in order to make sure the clipboard is // converted correctly. unsigned char eventType = (theEvent->message >> 24) & 0x00ff; if (eventType == mouseMovedMessage) { // I'm guessing we don't want to let these through unless the mouse is // in the modal dialog so we don't see rollover feedback in windows behind // the dialog. if ( where == inContent && inWindow ) *aForWindow = PR_TRUE; } if ( eventType == suspendResumeMessage ) { *aForWindow = PR_TRUE; if (theEvent->message & resumeFlag) { // divert it to our window if it isn't naturally if (!inWindow) { StPortSetter portSetter(window); theEvent->where.v = 0; theEvent->where.h = 0; ::LocalToGlobal(&theEvent->where); } } } break; } // case of which event type } else *aForWindow = PR_TRUE; return NS_OK; } //------------------------------------------------------------------------- // // Enable/disable this component // //------------------------------------------------------------------------- NS_IMETHODIMP nsWindow::Enable(PRBool bState) { mEnabled = bState; return NS_OK; } static Boolean we_are_front_process() { ProcessSerialNumber thisPSN; ProcessSerialNumber frontPSN; (void)::GetCurrentProcess(&thisPSN); if (::GetFrontProcess(&frontPSN) == noErr) { if ((frontPSN.highLongOfPSN == thisPSN.highLongOfPSN) && (frontPSN.lowLongOfPSN == thisPSN.lowLongOfPSN)) return true; } return false; } //------------------------------------------------------------------------- // // Set the focus on this component // //------------------------------------------------------------------------- NS_IMETHODIMP nsWindow::SetFocus(PRBool aRaise) { gEventDispatchHandler.SetFocus(this); // Here's where we see if there's a notification we need to remove if (gNotificationInstalled && we_are_front_process()) { (void)::NMRemove(&gNMRec); gNotificationInstalled = false; } return NS_OK; } //------------------------------------------------------------------------- // // Get this component font // //------------------------------------------------------------------------- nsIFontMetrics* nsWindow::GetFont(void) { return mFontMetrics; } //------------------------------------------------------------------------- // // Set this component font // //------------------------------------------------------------------------- NS_IMETHODIMP nsWindow::SetFont(const nsFont &aFont) { NS_IF_RELEASE(mFontMetrics); if (mContext) mContext->GetMetricsFor(aFont, mFontMetrics); return NS_OK; } //------------------------------------------------------------------------- // // Set the colormap of the window // //------------------------------------------------------------------------- NS_IMETHODIMP nsWindow::SetColorMap(nsColorMap *aColorMap) { //¥TODO // We may need to move this to nsMacWindow: // I'm not sure all the individual widgets // can have each their own colorMap on Mac. return NS_OK; } //------------------------------------------------------------------------- // // Set the widget's MenuBar. // Must be called after Create. // Releases a previously set nsIMenuBar // AddRefs the passed in nsIMenuBar // @param aMenuBar a pointer to an nsIMenuBar interface on an object // //------------------------------------------------------------------------- NS_IMETHODIMP nsWindow::SetMenuBar(nsIMenuBar * aMenuBar) { if (mMenuBar) mMenuBar->SetParent(nsnull); NS_IF_RELEASE(mMenuBar); NS_IF_ADDREF(aMenuBar); mMenuBar = aMenuBar; return NS_OK; } NS_IMETHODIMP nsWindow::ShowMenuBar(PRBool aShow) { // this may never be implemented on the Mac return NS_ERROR_FAILURE; } //------------------------------------------------------------------------- // // Get the widget's MenuBar. // //------------------------------------------------------------------------- nsIMenuBar* nsWindow::GetMenuBar() { return mMenuBar; } // // SetCursor // // Override to set the cursor on the mac // NS_METHOD nsWindow::SetCursor(nsCursor aCursor) { nsBaseWidget::SetCursor(aCursor); // allow the cursor to be set internally if we're in the bg, but // don't actually set it. if ( !nsToolkit::IsAppInForeground() ) return NS_OK; // mac specific cursor manipulation if (nsToolkit::HasAppearanceManager()) { short cursor = -1; switch (aCursor) { case eCursor_standard: cursor = kThemeArrowCursor; break; case eCursor_wait: cursor = kThemeWatchCursor; break; case eCursor_select: cursor = kThemeIBeamCursor; break; case eCursor_hyperlink: cursor = kThemePointingHandCursor; break; case eCursor_sizeWE: cursor = kThemeResizeLeftRightCursor; break; case eCursor_sizeNS: cursor = 129; break; case eCursor_sizeNW: cursor = 130; break; case eCursor_sizeSE: cursor = 131; break; case eCursor_sizeNE: cursor = 132; break; case eCursor_sizeSW: cursor = 133; break; case eCursor_arrow_north: cursor = 134; break; case eCursor_arrow_north_plus:cursor = 135; break; case eCursor_arrow_south: cursor = 136; break; case eCursor_arrow_south_plus:cursor = 137; break; case eCursor_arrow_west: cursor = 138; break; case eCursor_arrow_west_plus: cursor = 139; break; case eCursor_arrow_east: cursor = 140; break; case eCursor_arrow_east_plus: cursor = 141; break; case eCursor_crosshair: cursor = kThemeCrossCursor; break; case eCursor_move: cursor = kThemeOpenHandCursor; break; case eCursor_help: cursor = 143; break; case eCursor_copy: cursor = 144; break; // CSS3 case eCursor_alias: cursor = 145; break; case eCursor_context_menu: cursor = 146; break; case eCursor_cell: cursor = kThemePlusCursor; break; case eCursor_grab: cursor = kThemeOpenHandCursor; break; case eCursor_grabbing: cursor = kThemeClosedHandCursor; break; case eCursor_spinning: cursor = 149; break; // better than kThemeSpinningCursor case eCursor_count_up: cursor = kThemeCountingUpHandCursor; break; case eCursor_count_down: cursor = kThemeCountingDownHandCursor; break; case eCursor_count_up_down: cursor = kThemeCountingUpAndDownHandCursor; break; } if (cursor >= 0) { if (cursor >= 128) { nsMacResources::OpenLocalResourceFile(); ::SetCursor(*(::GetCursor(cursor))); nsMacResources::CloseLocalResourceFile(); } else ::SetThemeCursor(cursor); } } else { short cursor = -1; switch (aCursor) { case eCursor_standard: ::InitCursor(); break; case eCursor_wait: cursor = watchCursor; break; case eCursor_select: cursor = iBeamCursor; break; case eCursor_hyperlink: cursor = plusCursor; break; case eCursor_sizeWE: cursor = 128; break; case eCursor_sizeNS: cursor = 129; break; case eCursor_sizeNW: cursor = 130; break; case eCursor_sizeSE: cursor = 131; break; case eCursor_sizeNE: cursor = 132; break; case eCursor_sizeSW: cursor = 133; break; case eCursor_arrow_north: cursor = 134; break; case eCursor_arrow_north_plus:cursor = 135; break; case eCursor_arrow_south: cursor = 136; break; case eCursor_arrow_south_plus:cursor = 137; break; case eCursor_arrow_west: cursor = 138; break; case eCursor_arrow_west_plus: cursor = 139; break; case eCursor_arrow_east: cursor = 140; break; case eCursor_arrow_east_plus: cursor = 141; break; case eCursor_crosshair: cursor = crossCursor; break; case eCursor_move: cursor = 142; break; case eCursor_help: cursor = 143; break; case eCursor_copy: cursor = 144; break; // CSS3 case eCursor_alias: cursor = 145; break; case eCursor_context_menu: cursor = 146; break; case eCursor_cell: cursor = plusCursor; break; case eCursor_grab: cursor = 147; break; case eCursor_grabbing: cursor = 148; break; case eCursor_spinning: cursor = 149; break; case eCursor_count_up: cursor = watchCursor; break; case eCursor_count_down: cursor = watchCursor; break; case eCursor_count_up_down: cursor = watchCursor; break; } if (cursor > 0) { if (cursor >= 128) { nsMacResources::OpenLocalResourceFile(); ::SetCursor(*(::GetCursor(cursor))); nsMacResources::CloseLocalResourceFile(); } else ::SetCursor(*(::GetCursor(cursor))); } } return NS_OK; } // nsWindow :: SetCursor #pragma mark - //------------------------------------------------------------------------- // // Get this component dimension // //------------------------------------------------------------------------- NS_IMETHODIMP nsWindow::GetBounds(nsRect &aRect) { aRect = mBounds; return NS_OK; } NS_METHOD nsWindow::SetBounds(const nsRect &aRect) { nsresult rv = Inherited::SetBounds(aRect); if ( NS_SUCCEEDED(rv) ) CalcWindowRegions(); return rv; } NS_IMETHODIMP nsWindow::ConstrainPosition(PRInt32 *aX, PRInt32 *aY) { return NS_OK; } //------------------------------------------------------------------------- // // Move this component // aX and aY are in the parent widget coordinate system //------------------------------------------------------------------------- NS_IMETHODIMP nsWindow::Move(PRInt32 aX, PRInt32 aY) { if ((mBounds.x != aX) || (mBounds.y != aY)) { // Invalidate the current location (unless it's the top-level window) if (mParent != nsnull) Invalidate(PR_FALSE); // Set the bounds mBounds.x = aX; mBounds.y = aY; // Recalculate the regions CalcWindowRegions(); // Report the event ReportMoveEvent(); } return NS_OK; } //------------------------------------------------------------------------- // // Resize this component // //------------------------------------------------------------------------- NS_IMETHODIMP nsWindow::Resize(PRInt32 aWidth, PRInt32 aHeight, PRBool aRepaint) { if ((mBounds.width != aWidth) || (mBounds.height != aHeight)) { // Set the bounds mBounds.width = aWidth; mBounds.height = aHeight; // Recalculate the regions CalcWindowRegions(); // Invalidate the new location if (aRepaint) Invalidate(PR_FALSE); // Report the event ReportSizeEvent(); } else { // Recalculate the regions. We always need to do this, our parents may have // changed, hence changing our notion of visibility. We then also should make // sure that we invalidate ourselves correctly. Fixes bug 18240 (pinkerton). CalcWindowRegions(); if (aRepaint) Invalidate(PR_FALSE); } return NS_OK; } //------------------------------------------------------------------------- // // Resize this component // //------------------------------------------------------------------------- NS_IMETHODIMP 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::GetPreferredSize(PRInt32& aWidth, PRInt32& aHeight) { aWidth = mPreferredWidth; aHeight = mPreferredHeight; return NS_ERROR_FAILURE; } NS_METHOD nsWindow::SetPreferredSize(PRInt32 aWidth, PRInt32 aHeight) { mPreferredWidth = aWidth; mPreferredHeight = aHeight; return NS_OK; } //------------------------------------------------------------------------- // // //------------------------------------------------------------------------- NS_IMETHODIMP nsWindow::BeginResizingChildren(void) { mResizingChildren = PR_TRUE; mSaveVisible = mVisible; mVisible = PR_FALSE; return NS_OK; } //------------------------------------------------------------------------- // // //------------------------------------------------------------------------- NS_IMETHODIMP nsWindow::EndResizingChildren(void) { mResizingChildren = PR_FALSE; mVisible = mSaveVisible; CalcWindowRegions(); return NS_OK; } #pragma mark - #ifdef BLINK_DEBUGGING static Boolean caps_lock() { EventRecord event; ::OSEventAvail(0, &event); return ((event.modifiers & alphaLock) != 0); } static void blinkRect(Rect* r) { StRegionFromPool oldClip; if (oldClip != NULL) ::GetClip(oldClip); ::ClipRect(r); ::InvertRect(r); UInt32 end = ::TickCount() + 5; while (::TickCount() < end) ; ::InvertRect(r); if (oldClip != NULL) ::SetClip(oldClip); } static void blinkRgn(RgnHandle rgn) { StRegionFromPool oldClip; if (oldClip != NULL) ::GetClip(oldClip); ::SetClip(rgn); ::InvertRgn(rgn); UInt32 end = ::TickCount() + 5; while (::TickCount() < end) ; ::InvertRgn(rgn); if (oldClip != NULL) ::SetClip(oldClip); } #endif //------------------------------------------------------------------------- // // Invalidate this component's visible area // //------------------------------------------------------------------------- NS_IMETHODIMP nsWindow::Invalidate(PRBool aIsSynchronous) { nsRect area = mBounds; area.x = area.y = 0; Invalidate(area, aIsSynchronous); return NS_OK; } //------------------------------------------------------------------------- // // Invalidate this component's visible area // //------------------------------------------------------------------------- NS_IMETHODIMP nsWindow::Invalidate(const nsRect &aRect, PRBool aIsSynchronous) { if (!mWindowPtr) return NS_OK; nsRect wRect = aRect; wRect.MoveBy(mBounds.x, mBounds.y); // beard: this is required, see GetNativeData(NS_NATIVE_OFFSETX). LocalToWindowCoordinate(wRect); Rect macRect; nsRectToMacRect(wRect, macRect); StPortSetter portSetter(mWindowPtr); Rect savePortRect; ::GetWindowPortBounds(mWindowPtr, &savePortRect); ::SetOrigin(0, 0); #ifdef BLINK_DEBUGGING if (caps_lock()) ::blinkRect(&macRect); #endif ::InvalWindowRect(mWindowPtr, &macRect); if ( aIsSynchronous ) Update(); ::SetOrigin(savePortRect.left, savePortRect.top); return NS_OK; } //------------------------------------------------------------------------- // // Invalidate this component's visible area // //------------------------------------------------------------------------- NS_IMETHODIMP nsWindow::InvalidateRegion(const nsIRegion *aRegion, PRBool aIsSynchronous) { if (!mWindowPtr) return NS_OK; // copy invalid region into a working region. void* nativeRgn; aRegion->GetNativeRegion(nativeRgn); StRegionFromPool windowRgn; ::CopyRgn(RgnHandle(nativeRgn), windowRgn); // translate this region into window coordinates. PRInt32 offX, offY; this->CalcOffset(offX, offY); ::OffsetRgn(windowRgn, mBounds.x + offX, mBounds.y + offY); StPortSetter portSetter(mWindowPtr); Rect savePortRect; ::GetWindowPortBounds(mWindowPtr, &savePortRect); ::SetOrigin(0, 0); #ifdef BLINK_DEBUGGING if (caps_lock()) ::blinkRgn(windowRgn); #endif ::InvalWindowRgn(mWindowPtr, windowRgn); if ( aIsSynchronous ) Update(); ::SetOrigin(savePortRect.left, savePortRect.top); return NS_OK; } inline PRUint16 COLOR8TOCOLOR16(PRUint8 color8) { // return (color8 == 0xFF ? 0xFFFF : (color8 << 8)); return (color8 << 8) | color8; /* (color8 * 257) == (color8 * 0x0101) */ } //------------------------------------------------------------------------- // StartDraw // //------------------------------------------------------------------------- void nsWindow::StartDraw(nsIRenderingContext* aRenderingContext) { if (mDrawing) return; mDrawing = PR_TRUE; CalcWindowRegions(); //¥REVISIT if (aRenderingContext == nsnull) { // make sure we have a rendering context mTempRenderingContext = GetRenderingContext(); mTempRenderingContextMadeHere = PR_TRUE; } else { // if we already have a rendering context, save its state NS_IF_ADDREF(aRenderingContext); mTempRenderingContext = aRenderingContext; mTempRenderingContextMadeHere = PR_FALSE; mTempRenderingContext->PushState(); // set the environment to the current widget mTempRenderingContext->Init(mContext, this); } // set the widget font. nsMacControl implements SetFont, which is where // the font should get set. if (mFontMetrics) { mTempRenderingContext->SetFont(mFontMetrics); } // set the widget background and foreground colors nscolor color = GetBackgroundColor(); RGBColor macColor; macColor.red = COLOR8TOCOLOR16(NS_GET_R(color)); macColor.green = COLOR8TOCOLOR16(NS_GET_G(color)); macColor.blue = COLOR8TOCOLOR16(NS_GET_B(color)); ::RGBBackColor(&macColor); color = GetForegroundColor(); macColor.red = COLOR8TOCOLOR16(NS_GET_R(color)); macColor.green = COLOR8TOCOLOR16(NS_GET_G(color)); macColor.blue = COLOR8TOCOLOR16(NS_GET_B(color)); ::RGBForeColor(&macColor); mTempRenderingContext->SetColor(color); // just in case, set the rendering context color too } //------------------------------------------------------------------------- // EndDraw // //------------------------------------------------------------------------- void nsWindow::EndDraw() { if (! mDrawing) return; mDrawing = PR_FALSE; if (mTempRenderingContextMadeHere) { PRBool clipEmpty; mTempRenderingContext->PopState(clipEmpty); } NS_RELEASE(mTempRenderingContext); } //------------------------------------------------------------------------- // // //------------------------------------------------------------------------- void nsWindow::Flash(nsPaintEvent &aEvent) { #ifdef NS_DEBUG Rect flashRect; if (debug_WantPaintFlashing() && aEvent.rect ) { ::SetRect ( &flashRect, aEvent.rect->x, aEvent.rect->y, aEvent.rect->x + aEvent.rect->width, aEvent.rect->y + aEvent.rect->height ); StPortSetter portSetter(mWindowPtr); unsigned long endTicks; ::InvertRect ( &flashRect ); ::Delay(10, &endTicks); ::InvertRect ( &flashRect ); } #endif } // // OnPaint // // Dummy impl, meant to be overridden // PRBool nsWindow::OnPaint(nsPaintEvent &event) { return PR_TRUE; } //------------------------------------------------------------------------- // Update // // Redraw this widget. // // We draw the widget between BeginUpdate and EndUpdate because some // operations go much faster when the visRgn contains what needs to be // painted. Then we restore the original updateRgn and validate this // widget's rectangle. //------------------------------------------------------------------------- NS_IMETHODIMP nsWindow::Update() { if (! mVisible || !mWindowPtr) return NS_OK; static PRBool reentrant = PR_FALSE; if (reentrant) HandleUpdateEvent(nil); else { reentrant = PR_TRUE; StRegionFromPool regionToValidate; if (!regionToValidate) return NS_ERROR_OUT_OF_MEMORY; // make a copy of the window update rgn StRegionFromPool saveUpdateRgn; if (!saveUpdateRgn) return NS_ERROR_OUT_OF_MEMORY; if(mWindowPtr) ::GetWindowUpdateRegion ( mWindowPtr, saveUpdateRgn ); // draw the widget StPortSetter portSetter(mWindowPtr); ::BeginUpdate(mWindowPtr); HandleUpdateEvent(regionToValidate); ::EndUpdate(mWindowPtr); // restore the window update rgn #if TARGET_CARBON //¥PINK - hrm, can't do this in Carbon for re-entrancy reasons // ::CopyRgn(saveUpdateRgn, ((WindowRecord*)mWindowPtr)->updateRgn); ::InvalWindowRgn(mWindowPtr, saveUpdateRgn); #else ::CopyRgn(saveUpdateRgn, ((WindowRecord*)mWindowPtr)->updateRgn); #endif ::ValidWindowRgn(mWindowPtr, regionToValidate); reentrant = PR_FALSE; } return NS_OK; } #pragma mark - static Boolean control_key_down() { EventRecord event; ::EventAvail(0, &event); return (event.modifiers & controlKey) != 0; } static long long microseconds() { unsigned long long micros; Microseconds((UnsignedWide*)µs); return micros; } #if TARGET_CARBON // // UpdateRect // // Called once for every rect in the update region. Send a paint event to Gecko to handle // this. |refCon| contains the |nsWindow| being updated. // OSStatus nsWindow :: PaintUpdateRectProc (UInt16 message, RgnHandle rgn, const Rect *inDirtyRect, void *refCon) { if (message == kQDRegionToRectsMsgParse) { nsWindow* self = NS_REINTERPRET_CAST(nsWindow*, refCon); Rect dirtyRect = *inDirtyRect; nsCOMPtr renderingContext ( dont_AddRef(self->GetRenderingContext()) ); if (renderingContext) { nsRect bounds = self->mBounds; self->LocalToWindowCoordinate(bounds); // determine the rect to draw ::OffsetRect(&dirtyRect, -bounds.x, -bounds.y); nsRect rect ( dirtyRect.left, dirtyRect.top, dirtyRect.right - dirtyRect.left, dirtyRect.bottom - dirtyRect.top ); // update the widget self->UpdateWidget(rect, renderingContext); } } return noErr; } // PaintUpdateRectProc // // AddRectToArrayProc // // Add each rect to an array so we can post-process them. |inArray| is a // pointer to the TRectArray we're adding to. // OSStatus nsWindow :: AddRectToArrayProc (UInt16 message, RgnHandle rgn, const Rect *inDirtyRect, void *inArray) { if (message == kQDRegionToRectsMsgParse) { NS_ASSERTION ( inArray, "You better pass an array!" ); TRectArray* rectArray = NS_REINTERPRET_CAST(TRectArray*, inArray); rectArray->mRectList[rectArray->Count()] = *inDirtyRect; ++rectArray->mNumRects; } return noErr; } // // CountUpdateRectProc // // Used to count the number of rects in a region. |refCon| is a pointer to a // counter. We just increment it every time we're called and by the end we'll have // the number of rects. // OSStatus nsWindow :: CountRectProc (UInt16 message, RgnHandle rgn, const Rect *inDirtyRect, void *refCon) { if (message == kQDRegionToRectsMsgParse) { NS_ASSERTION ( refCon, "You better pass a counter!" ); (*NS_REINTERPRET_CAST(long*, refCon))++; // increment } return noErr; } #endif #if PINK_PROFILING static Boolean KeyDown(const UInt8 theKey); static Boolean KeyDown(const UInt8 theKey) { KeyMap map; GetKeys(map); return ((*((UInt8 *)map + (theKey >> 3)) >> (theKey & 7)) & 1) != 0; } #endif // // UpdateRect // // Called once for every rect in the update region. Send a paint event to Gecko to handle // this. |inData| contains the |nsWindow| being updated. // void nsWindow :: PaintUpdateRect (Rect *inDirtyRect, void* inData) { nsWindow* self = NS_REINTERPRET_CAST(nsWindow*, inData); Rect dirtyRect = *inDirtyRect; nsCOMPtr renderingContext ( dont_AddRef(self->GetRenderingContext()) ); if (renderingContext) { nsRect bounds = self->mBounds; self->LocalToWindowCoordinate(bounds); // determine the rect to draw ::OffsetRect(&dirtyRect, -bounds.x, -bounds.y); nsRect rect ( dirtyRect.left, dirtyRect.top, dirtyRect.right - dirtyRect.left, dirtyRect.bottom - dirtyRect.top ); // update the widget self->UpdateWidget(rect, renderingContext); } } // PaintUpdateRect // // CountRect // // Used to count the number of rects in a region. |refCon| is a pointer to a // counter. We just increment it every time we're called and by the end we'll have // the number of rects. // void nsWindow :: CountRect ( Rect *inDirtyRect, void *refCon ) { NS_ASSERTION ( refCon, "You better pass a counter!" ); (*NS_REINTERPRET_CAST(long*, refCon))++; // increment } // // AddRectToArray // // Add each rect to an array so we can post-process them. |inArray| is a // pointer to the TRectArray we're adding to. // void nsWindow :: AddRectToArray ( Rect* inDirtyRect, void* inArray ) { NS_ASSERTION ( inArray, "You better pass an array!" ); TRectArray* rectArray = NS_REINTERPRET_CAST(TRectArray*, inArray); rectArray->mRectList[rectArray->Count()] = *inDirtyRect; ++rectArray->mNumRects; } // AddRectToArray //------------------------------------------------------------------------- // HandleUpdateEvent // // Called by the event handler to redraw the top-level widget. // Must be called between BeginUpdate/EndUpdate: the window visRgn // is expected to be set to whatever needs to be drawn. //------------------------------------------------------------------------- nsresult nsWindow::HandleUpdateEvent(RgnHandle regionToValidate) { #if PINK_PROFILING if (KeyDown(0x39)) // press [caps lock] to start the profile ProfileStart(); else ProfileStop(); #endif if (! mVisible) return NS_OK; // make sure the port is set and origin is (0, 0). StPortSetter portSetter(mWindowPtr); ::SetOrigin(0, 0); // get the damaged region from the OS StRegionFromPool damagedRgn; if (!damagedRgn) return NS_ERROR_OUT_OF_MEMORY; ::GetPortVisibleRegion(GrafPtr(GetWindowPort(mWindowPtr)), damagedRgn); #ifdef PAINT_DEBUGGING blinkRgn(damagedRgn); #endif // calculate the update region relatively to the window port rect // (at this point, the grafPort origin should always be 0,0 // so mWindowRegion has to be converted to window coordinates) StRegionFromPool updateRgn; if (!updateRgn) return NS_ERROR_OUT_OF_MEMORY; ::CopyRgn(mWindowRegion, updateRgn); nsRect bounds = mBounds; LocalToWindowCoordinate(bounds); ::OffsetRgn(updateRgn, bounds.x, bounds.y); #ifdef PAINT_DEBUGGING blinkRgn(updateRgn); #endif // check if the update region is visible ::SectRgn(damagedRgn, updateRgn, updateRgn); if (!::EmptyRgn(updateRgn)) { #if DEBUG // measure the time it takes to refresh the window, if the control key is down. unsigned long long start, finish; Boolean measure_duration = control_key_down(); if (measure_duration) start = microseconds(); #endif // Iterate over each rect in the region, sending a paint event for each. Carbon // has a routine for this, pre-carbon doesn't so we roll our own. If the region // is very complicated (more than 15 pieces), just use a bounding box. const int kMaxUpdateRects = 15; // the most rects we'll try to deal with const int kRectsBeforeBoundingBox = 10; // if we have more than this, just do bounding box int numRects = 0; #if TARGET_CARBON QDRegionToRects ( updateRgn, kQDParseRegionFromTopLeft, sCountRectProc, &numRects ); if ( numRects <= kMaxUpdateRects ) { Rect rectList[kMaxUpdateRects]; TRectArray rectWrapper ( rectList ); // compile a list of rectangles QDRegionToRects ( updateRgn, kQDParseRegionFromTopLeft, sAddRectToArrayProc, &rectWrapper ); // amalgamate adjoining rects into a single rect. This // may over-draw a little, but will prevent us from going down into // the layout engine for lots of little 1-pixel wide rects. if ( numRects > 1 ) CombineRects ( rectWrapper ); // now paint 'em! (|numRects| may be invalid now, so check count again). If // we're above a certain threshold, just bail and do bounding box if ( rectWrapper.Count() < kRectsBeforeBoundingBox ) { for ( int i = 0; i < rectWrapper.Count(); ++i ) PaintUpdateRectProc (kQDRegionToRectsMsgParse, updateRgn, &rectList[i], this ); } else { Rect boundingBox; ::GetRegionBounds(updateRgn, &boundingBox); PaintUpdateRectProc ( kQDRegionToRectsMsgParse, updateRgn, &boundingBox, this ); } } else { Rect boundingBox; ::GetRegionBounds(updateRgn, &boundingBox); PaintUpdateRect ( &boundingBox, this ); } #else EachRegionRect ( updateRgn, CountRect, &numRects ); if ( numRects <= kMaxUpdateRects ) { Rect rectList[kMaxUpdateRects]; TRectArray rectWrapper ( rectList ); // compile a list of rectangles EachRegionRect ( updateRgn, AddRectToArray, &rectWrapper ); // amalgamate adjoining rects into a single rect. This // may over-draw a little, but will prevent us from going down into // the layout engine for lots of little 1-pixel wide rects. if ( numRects > 1 ) CombineRects ( rectWrapper ); // now paint 'em! (|numRects| may be invalid now, so check count again). If // we're above a certain threshold, just bail and do bounding box if ( rectWrapper.Count() < kRectsBeforeBoundingBox ) { for ( int i = 0; i < rectWrapper.Count(); ++i ) PaintUpdateRect ( &rectList[i], this ); } else { Rect boundingBox; ::GetRegionBounds(updateRgn, &boundingBox); PaintUpdateRect ( &boundingBox, this ); } } else { Rect boundingBox; ::GetRegionBounds(updateRgn, &boundingBox); PaintUpdateRect ( &boundingBox, this ); } #endif #if DEBUG if (measure_duration) { finish = microseconds(); printf("update took %g microseconds.\n", double(finish - start)); } #endif // Copy updateRgn to regionToValidate if (regionToValidate) ::CopyRgn(updateRgn, regionToValidate); } #if PINK_PROFILING ProfileSuspend(); ProfileStop(); #endif return NS_OK; } // // SortRectsLeftToRight // // |inRectArray| is an array of mac |Rect|s, sort them by increasing |left| // values (so they are sorted left to right). I feel so dirty for using a // bubble sort, but darn if it isn't simple and we're only going to have // about 10 of these rects at maximum (that's a fairly compilicated update // region). // void nsWindow :: SortRectsLeftToRight ( TRectArray & inRectArray ) { PRInt32 numRects = inRectArray.Count(); for ( int i = 0; i < numRects - 1; ++i ) { for ( int j = i+1; j < numRects; ++j ) { if ( inRectArray.mRectList[j].left < inRectArray.mRectList[i].left ) { Rect temp = inRectArray.mRectList[i]; inRectArray.mRectList[i] = inRectArray.mRectList[j]; inRectArray.mRectList[j] = temp; } } } } // SortRectsLeftToRight // // CombineRects // // When breaking up our update region into rects, invariably we end up with lots // of tall, thin rectangles that are right next to each other (the drop // shadow of windows are an extreme case). Combine adjacent rectangles if the // wasted area (the difference between area of the two rects and the bounding // box of the two joined rects) is small enough. // // As a side effect, the rects will be sorted left->right. // void nsWindow :: CombineRects ( TRectArray & rectArray ) { const float kCombineThresholdRatio = 0.50; // 50% // We assume the rects are sorted left to right below, so sort 'em. SortRectsLeftToRight ( rectArray ); // Here's basically what we're doing: // // compute the area of X and X+1 // compute area of the bounding rect (topLeft of X and bottomRight of X+1) // if ( ratio of combined areas to bounding box is > 50% ) // make bottomRight of X be bottomRight of X+1 // delete X+1 from array and don't advance X, // otherwise move along to X+1 int i = 0; while ( i < rectArray.Count()-1) { Rect* curr = &rectArray.mRectList[i]; Rect* next = &rectArray.mRectList[i+1]; // compute areas of current and next rects int currArea = (curr->right - curr->left) * (curr->bottom - curr->top); int nextArea = (next->right - next->left) * (next->bottom - next->top); // compute the bounding box and its area Rect boundingBox; ::UnionRect ( curr, next, &boundingBox ); int boundingRectArea = (boundingBox.right - boundingBox.left) * (boundingBox.bottom - boundingBox.top); // determine if we should combine the rects, based on if the ratio of the // combined areas to the bounding rect's area is above some threshold. if ( (currArea + nextArea) / (float)boundingRectArea > kCombineThresholdRatio ) { // we're combining, so combine the rects, delete the next rect (i+1), remove it from // the array, and _don't_ advance to the next rect. // make the current rectangle the bounding box and shift everything from // i+2 over. *curr = boundingBox; for ( int j = i+1; j < rectArray.Count()-1; ++j ) rectArray.mRectList[j] = rectArray.mRectList[j+1]; --rectArray.mNumRects; } // if we should combine else ++i; } // foreach rect } // CombineRects #pragma mark - //------------------------------------------------------------------------- // // //------------------------------------------------------------------------- void nsWindow::UpdateWidget(nsRect& aRect, nsIRenderingContext* aContext) { if (! mVisible) return; // initialize the paint event nsPaintEvent paintEvent; paintEvent.eventStructType = NS_PAINT_EVENT; // nsEvent paintEvent.message = NS_PAINT; paintEvent.widget = this; // nsGUIEvent paintEvent.nativeMsg = NULL; paintEvent.renderingContext = aContext; // nsPaintEvent paintEvent.rect = &aRect; // draw the widget StartDraw(aContext); if ( OnPaint(paintEvent) ) { nsEventStatus eventStatus; DispatchWindowEvent(paintEvent,eventStatus); if(eventStatus != nsEventStatus_eIgnore) Flash(paintEvent); } EndDraw(); // beard: Since we clip so aggressively, drawing from front to back should work, // and it does for the most part. However; certain cases, such as overlapping // areas that are handled by different view managers, don't properly clip siblings. #ifdef FRONT_TO_BACK # define FIRST_CHILD(children) (children->Last()) # define NEXT_CHILD(children) (children->Prev()) #else # define FIRST_CHILD(children) (children->First()) # define NEXT_CHILD(children) (children->Next()) #endif // recursively draw the children nsCOMPtr children(getter_AddRefs((nsIBidirectionalEnumerator*)GetChildren())); if (children) { FIRST_CHILD(children); do { nsISupports* child; if (NS_SUCCEEDED(children->CurrentItem(&child))) { nsWindow* childWindow = static_cast(static_cast(child)); nsRect childBounds; childWindow->GetBounds(childBounds); // redraw only the intersection of the child rect and the update rect nsRect intersection; if (intersection.IntersectRect(aRect, childBounds)) { intersection.MoveBy(-childBounds.x, -childBounds.y); childWindow->UpdateWidget(intersection, aContext); } NS_RELEASE(child); } } while (NS_SUCCEEDED(NEXT_CHILD(children))); } #undef FIRST_CHILD #undef NEXT_CHILD } // // ScrollBits // // ::ScrollRect() unfortunately paints the invalidated area with the // background pattern. This causes lots of ugly flashing and makes us look // pretty bad. Instead, we roll our own ::ScrollRect() by using ::CopyBits() // to scroll the image in the view and then set the update // rgn appropriately so that the compositor can blit it to the screen. // // This will also work with system floating windows over the area that is // scrolling. // // ¥¥¥¥ This routine really needs to be Carbonized!!!! It is nowhere close, // ¥¥¥¥ even though there are a couple of carbon ifdefs here already. // void nsWindow :: ScrollBits ( Rect & inRectToScroll, PRInt32 inLeftDelta, PRInt32 inTopDelta ) { // Get Frame in local coords from clip rect (there might be a border around view) StRegionFromPool clipRgn; if ( !clipRgn ) return; ::GetClip(clipRgn); ::SectRgn(clipRgn, mVisRegion, clipRgn); Rect frame; ::GetRegionBounds(clipRgn, &frame); StRegionFromPool totalVisRgn; if ( !totalVisRgn ) return; ::RectRgn(totalVisRgn, &frame); // compute the source and destination of copybits Rect source = inRectToScroll; SectRect(&source, &frame, &source); Rect dest = source; ::OffsetRect(&dest, inLeftDelta, inTopDelta); // compute the area that is to be updated by subtracting the dest from the visible area StRegionFromPool destRgn; if ( !destRgn ) return; ::RectRgn(destRgn, &dest); StRegionFromPool updateRgn; if ( !updateRgn ) return; ::RectRgn(updateRgn, &frame); ::DiffRgn (updateRgn, destRgn, updateRgn); #if TARGET_CARBON RgnHandle visRgn = ::NewRgn(); ::GetPortVisibleRegion(::GetWindowPort(mWindowPtr), visRgn); if (::EmptyRgn(visRgn)) { PixMapHandle portPixH = ::GetPortPixMap(::GetWindowPort(mWindowPtr)); ::HLock((Handle) portPixH); ::CopyBits ( *((BitMapHandle) portPixH), *((BitMapHandle) portPixH), &source, &dest, srcCopy, nil); ::HUnlock((Handle) portPixH); } #else if(::EmptyRgn(mWindowPtr->visRgn)) { ::CopyBits ( &mWindowPtr->portBits, &mWindowPtr->portBits, &source, &dest, srcCopy, nil); } #endif else { // compute the non-visible region StRegionFromPool nonVisibleRgn; if ( !nonVisibleRgn ) return; #if TARGET_CARBON ::DiffRgn ( totalVisRgn, visRgn, nonVisibleRgn ); #else ::DiffRgn ( totalVisRgn, mWindowPtr->visRgn, nonVisibleRgn ); #endif // compute the extra area that may need to be updated // scoll the non-visible region to determine what needs updating ::OffsetRgn ( nonVisibleRgn, inLeftDelta, inTopDelta ); // calculate a mask region to not copy the non-visble portions of the window from the port StRegionFromPool copyMaskRgn; if ( !copyMaskRgn ) return; ::DiffRgn(totalVisRgn, nonVisibleRgn, copyMaskRgn); // use copybits to simulate a ScrollRect() RGBColor black = { 0, 0, 0 }; RGBColor white = { 0xFFFF, 0xFFFF, 0xFFFF } ; ::RGBForeColor(&black); ::RGBBackColor(&white); ::PenNormal(); #if TARGET_CARBON PixMapHandle portPixH = ::GetPortPixMap(::GetWindowPort(mWindowPtr)); ::HLock((Handle) portPixH); ::CopyBits ( *((BitMapHandle) portPixH), *((BitMapHandle) portPixH), &source, &dest, srcCopy, copyMaskRgn); ::HUnlock((Handle) portPixH); #else ::CopyBits ( &mWindowPtr->portBits, &mWindowPtr->portBits, &source, &dest, srcCopy, copyMaskRgn); #endif // union the update regions together and invalidate them ::UnionRgn(nonVisibleRgn, updateRgn, updateRgn); } #if TARGET_CARBON ::DisposeRgn(visRgn); #endif // If the region to be scrolled contains regions which are currently dirty, // we must scroll those too, and union them with the updateRgn. #if !TARGET_CARBON // get a copy of the dirty region StRegionFromPool winUpdateRgn; if (!winUpdateRgn) return; ::BeginUpdate(mWindowPtr); ::CopyRgn(mWindowPtr->visRgn, winUpdateRgn); ::EndUpdate(mWindowPtr); StRegionFromPool dirtyRgn; if (!dirtyRgn) return; // get only the part of the dirtyRgn that intersects the frame ::SectRgn(winUpdateRgn, totalVisRgn, dirtyRgn); // offset by the amount scrolled ::OffsetRgn(dirtyRgn, inLeftDelta, inTopDelta); // now intersect with the frame again ::SectRgn(dirtyRgn, totalVisRgn, dirtyRgn); // and add it to the dirty region ::UnionRgn(updateRgn, dirtyRgn, updateRgn); // we also need to re-dirty the dirty rgn outside out frame, // since BeginUpdate/EndUpdate cleared it. ::DiffRgn(winUpdateRgn, totalVisRgn, winUpdateRgn); // and add it to the dirty region ::UnionRgn(updateRgn, winUpdateRgn, updateRgn); #endif //printf("*******scrolling invalidating %ld %ld %ld %ld\n", (**updateRgn).rgnBBox.top, (**updateRgn).rgnBBox.left, (**updateRgn).rgnBBox.bottom, // (**updateRgn).rgnBBox.right); ::InvalWindowRgn(mWindowPtr, updateRgn); } //------------------------------------------------------------------------- // // Scroll the bits of a window // //------------------------------------------------------------------------- NS_IMETHODIMP nsWindow::Scroll(PRInt32 aDx, PRInt32 aDy, nsRect *aClipRect) { if (! mVisible) return NS_OK; nsRect scrollRect; // If the clipping region is non-rectangular, just force a full update, sorry. // XXX ? if (!IsRegionRectangular(mWindowRegion)) { Invalidate(PR_TRUE); goto scrollChildren; } //-------- // Scroll this widget if (aClipRect) scrollRect = *aClipRect; else { scrollRect = mBounds; scrollRect.x = scrollRect.y = 0; } Rect macRect; nsRectToMacRect(scrollRect, macRect); StartDraw(); #if 1 // Clip to the windowRegion instead of the visRegion (note: the visRegion // is equal to the windowRegion minus the children). The result is that // ScrollRect() scrolls the visible bits of this widget as well as its children. ::SetClip(mWindowRegion); // Scroll the bits now. We've rolled our own because ::ScrollRect looks ugly ScrollBits(macRect,aDx,aDy); #else StRegionFromPool updateRgn; ::ScrollRect(&macRect, aDx, aDy, updateRgn); ::InvalRgn(updateRgn); #endif EndDraw(); scrollChildren: //-------- // Scroll the children nsCOMPtr children ( getter_AddRefs(GetChildren()) ); if (children) { children->First(); do { nsISupports* child; if (NS_SUCCEEDED(children->CurrentItem(&child))) { nsWindow* childWindow = static_cast(static_cast(child)); NS_RELEASE(child); nsRect bounds; childWindow->GetBounds(bounds); bounds.x += aDx; bounds.y += aDy; childWindow->SetBounds(bounds); } } while (NS_SUCCEEDED(children->Next())); } // recalculate the window regions CalcWindowRegions(); return NS_OK; } //------------------------------------------------------------------------- // // //------------------------------------------------------------------------- PRBool nsWindow::ConvertStatus(nsEventStatus aStatus) { switch (aStatus) { case nsEventStatus_eIgnore: return(PR_FALSE); case nsEventStatus_eConsumeNoDefault: return(PR_TRUE); // don't do default processing case nsEventStatus_eConsumeDoDefault: return(PR_FALSE); default: NS_ASSERTION(0, "Illegal nsEventStatus enumeration value"); break; } return(PR_FALSE); } //------------------------------------------------------------------------- // // Invokes callback and ProcessEvent method on Event Listener object // //------------------------------------------------------------------------- NS_IMETHODIMP nsWindow::DispatchEvent(nsGUIEvent* event, nsEventStatus& aStatus) { aStatus = nsEventStatus_eIgnore; if (! mDestructorCalled) { nsIWidget* aWidget = event->widget; NS_IF_ADDREF(aWidget); if (nsnull != mMenuListener){ if(NS_MENU_EVENT == event->eventStructType) aStatus = mMenuListener->MenuSelected( static_cast(*event) ); } if (mEventCallback) aStatus = (*mEventCallback)(event); // Dispatch to event listener if event was not consumed if ((aStatus != nsEventStatus_eConsumeNoDefault) && (mEventListener != nsnull)) aStatus = mEventListener->ProcessEvent(*event); NS_IF_RELEASE(aWidget); } return NS_OK; } //------------------------------------------------------------------------- PRBool nsWindow::DispatchWindowEvent(nsGUIEvent &event) { nsEventStatus status; DispatchEvent(&event, status); return ConvertStatus(status); } //------------------------------------------------------------------------- PRBool nsWindow::DispatchWindowEvent(nsGUIEvent &event,nsEventStatus &aStatus) { DispatchEvent(&event, aStatus); return ConvertStatus(aStatus); } //------------------------------------------------------------------------- // // Deal with all sort of mouse event // //------------------------------------------------------------------------- PRBool nsWindow::DispatchMouseEvent(nsMouseEvent &aEvent) { PRBool result = PR_FALSE; if (nsnull == mEventCallback && nsnull == mMouseListener) { return result; } // call the event callback if (nsnull != mEventCallback) { result = (DispatchWindowEvent(aEvent)); return result; } if (nsnull != mMouseListener) { switch (aEvent.message) { case NS_MOUSE_MOVE: { result = ConvertStatus(mMouseListener->MouseMoved(aEvent)); nsRect rect; GetBounds(rect); if (rect.Contains(aEvent.point.x, aEvent.point.y)) { //if (mWindowPtr == NULL || mWindowPtr != this) //{ // printf("Mouse enter"); //mCurrentWindow = this; //} } else { // printf("Mouse exit"); } } break; case NS_MOUSE_LEFT_BUTTON_DOWN: case NS_MOUSE_MIDDLE_BUTTON_DOWN: case NS_MOUSE_RIGHT_BUTTON_DOWN: result = ConvertStatus(mMouseListener->MousePressed(aEvent)); break; case NS_MOUSE_LEFT_BUTTON_UP: case NS_MOUSE_MIDDLE_BUTTON_UP: case NS_MOUSE_RIGHT_BUTTON_UP: result = ConvertStatus(mMouseListener->MouseReleased(aEvent)); result = ConvertStatus(mMouseListener->MouseClicked(aEvent)); break; } // switch } return result; } #pragma mark - //------------------------------------------------------------------------- // // //------------------------------------------------------------------------- PRBool nsWindow::ReportDestroyEvent() { // nsEvent nsGUIEvent moveEvent; moveEvent.eventStructType = NS_GUI_EVENT; moveEvent.message = NS_DESTROY; moveEvent.point.x = 0; moveEvent.point.y = 0; moveEvent.time = PR_IntervalNow(); // nsGUIEvent moveEvent.widget = this; moveEvent.nativeMsg = nsnull; // dispatch event return (DispatchWindowEvent(moveEvent)); } //------------------------------------------------------------------------- // // //------------------------------------------------------------------------- PRBool nsWindow::ReportMoveEvent() { // nsEvent nsGUIEvent moveEvent; moveEvent.eventStructType = NS_GUI_EVENT; moveEvent.message = NS_MOVE; moveEvent.point.x = mBounds.x; moveEvent.point.y = mBounds.y; moveEvent.time = PR_IntervalNow(); // nsGUIEvent moveEvent.widget = this; moveEvent.nativeMsg = nsnull; // dispatch event return (DispatchWindowEvent(moveEvent)); } //------------------------------------------------------------------------- // // //------------------------------------------------------------------------- PRBool nsWindow::ReportSizeEvent() { // nsEvent nsSizeEvent sizeEvent; sizeEvent.eventStructType = NS_SIZE_EVENT; sizeEvent.message = NS_SIZE; sizeEvent.point.x = 0; sizeEvent.point.y = 0; sizeEvent.time = PR_IntervalNow(); // nsGUIEvent sizeEvent.widget = this; sizeEvent.nativeMsg = nsnull; // nsSizeEvent sizeEvent.windowSize = &mBounds; sizeEvent.mWinWidth = mBounds.width; sizeEvent.mWinHeight = mBounds.height; // dispatch event return(DispatchWindowEvent(sizeEvent)); } #pragma mark - //------------------------------------------------------------------------- // // //------------------------------------------------------------------------- void nsWindow::CalcWindowRegions() { //------ // calculate the window region if (mWindowRegion == nsnull) { mWindowRegion = ::NewRgn(); if (mWindowRegion == nsnull) return; } ::SetRectRgn(mWindowRegion, 0, 0, mBounds.width, mBounds.height); // intersect with all the parents nsWindow* parent = (nsWindow*)mParent; nsPoint origin(-mBounds.x, -mBounds.y); while (parent) { if (parent->mWindowRegion) { ::OffsetRgn(parent->mWindowRegion, origin.x, origin.y); ::SectRgn(mWindowRegion, parent->mWindowRegion, mWindowRegion); ::OffsetRgn(parent->mWindowRegion, -origin.x, -origin.y); } origin.x -= parent->mBounds.x; origin.y -= parent->mBounds.y; parent = (nsWindow*)parent->mParent; } //------ // calculate the visible region if (mVisRegion == nsnull) { mVisRegion = ::NewRgn(); if (mVisRegion == nsnull) return; } ::CopyRgn(mWindowRegion, mVisRegion); // clip the children out of the visRegion nsCOMPtr children ( getter_AddRefs(GetChildren()) ); if (children) { StRegionFromPool childRgn; if (childRgn != nsnull) { children->First(); do { nsISupports* child; if (NS_SUCCEEDED(children->CurrentItem(&child))) { nsWindow* childWindow = static_cast(static_cast(child)); NS_RELEASE(child); PRBool visible; childWindow->IsVisible(visible); if (visible) { nsRect childRect; childWindow->GetBounds(childRect); Rect macRect; ::SetRect(&macRect, childRect.x, childRect.y, childRect.XMost(), childRect.YMost()); ::RectRgn(childRgn, &macRect); ::DiffRgn(mVisRegion, childRgn, mVisRegion); } } } while (NS_SUCCEEDED(children->Next())); } } } //------------------------------------------------------------------------- /* * @update dc 08/28/98 * @param aTheRegion -- The region to intersect with for this widget * @return PR_TRUE if the these regions intersect */ PRBool nsWindow::RgnIntersects(RgnHandle aTheRegion, RgnHandle aIntersectRgn) { ::SectRgn(aTheRegion, this->mWindowRegion, aIntersectRgn); return (::EmptyRgn(aIntersectRgn) != false); } //------------------------------------------------------------------------- /* Calculate the x and y offsets for this particular widget * @update ps 09/22/98 * @param aX -- x offset amount * @param aY -- y offset amount * @return NOTHING */ NS_IMETHODIMP nsWindow::CalcOffset(PRInt32 &aX,PRInt32 &aY) { aX = aY = 0; nsIWidget* grandParent; nsIWidget* theParent = this->GetParent(); while (theParent) { nsRect theRect; theParent->GetBounds(theRect); aX += theRect.x; aY += theRect.y; grandParent = theParent->GetParent(); NS_IF_RELEASE(theParent); theParent = grandParent; } return NS_OK; } //------------------------------------------------------------------------- // PointInWidget // Find if a point in local coordinates is inside this object //------------------------------------------------------------------------- PRBool nsWindow::PointInWidget(Point aThePoint) { // get the origin in local coordinates nsPoint widgetOrigin(0, 0); LocalToWindowCoordinate(widgetOrigin); // get rectangle relatively to the parent nsRect widgetRect; GetBounds(widgetRect); // convert the topLeft corner to local coordinates widgetRect.MoveBy(widgetOrigin.x, widgetOrigin.y); // finally tell whether it's a hit return(widgetRect.Contains(aThePoint.h, aThePoint.v)); } //------------------------------------------------------------------------- // FindWidgetHit // Recursively look for the widget hit // @param aParent -- parent widget. // @param aThePoint -- a point in local coordinates to test for the hit. //------------------------------------------------------------------------- nsWindow* nsWindow::FindWidgetHit(Point aThePoint) { if (!mVisible || !PointInWidget(aThePoint)) return nsnull; nsWindow* widgetHit = this; nsCOMPtr normalEnum ( getter_AddRefs(GetChildren()) ); nsCOMPtr children ( do_QueryInterface(normalEnum) ); if (children) { // traverse through all the nsWindows to find out who got hit, lowest level of course children->Last(); do { nsISupports* child; if (NS_SUCCEEDED(children->CurrentItem(&child))) { nsWindow* childWindow = static_cast(static_cast(child)); NS_RELEASE(child); nsWindow* deeperHit = childWindow->FindWidgetHit(aThePoint); if (deeperHit) { widgetHit = deeperHit; break; } } } while (NS_SUCCEEDED(children->Prev())); } return widgetHit; } #pragma mark - //------------------------------------------------------------------------- // WidgetToScreen // Walk up the parent tree, converting the given rect to global coordinates. // This is similiar to CalcOffset() but we can't use GetBounds() because it // only knows how to give us local coordinates. // @param aLocalRect -- rect in local coordinates of this widget // @param aGlobalRect -- |aLocalRect| in global coordinates //------------------------------------------------------------------------- NS_IMETHODIMP nsWindow::WidgetToScreen(const nsRect& aLocalRect, nsRect& aGlobalRect) { aGlobalRect = aLocalRect; nsIWidget* theParent = this->GetParent(); if ( theParent ) { // Recursive case // // Convert the local rect to global, except for this level. theParent->WidgetToScreen(aLocalRect, aGlobalRect); NS_RELEASE(theParent); // the offset from our parent is in the x/y of our bounding rect nsRect myBounds; GetBounds(myBounds); aGlobalRect.MoveBy(myBounds.x, myBounds.y); } else { // Base case of recursion // // When there is no parent, we're at the top level window. Use // the origin (shifted into global coordinates) to find the offset. StPortSetter portSetter(mWindowPtr); ::SetOrigin(0,0); // convert origin into global coords and shift output rect by that ammount Point origin = {0, 0}; ::LocalToGlobal ( &origin ); aGlobalRect.MoveBy ( origin.h, origin.v ); } return NS_OK; } //------------------------------------------------------------------------- // ScreenToWidget // Walk up the parent tree, converting the given rect to local coordinates. // @param aGlobalRect -- rect in screen coordinates // @param aLocalRect -- |aGlobalRect| in coordinates of this widget //------------------------------------------------------------------------- NS_IMETHODIMP nsWindow::ScreenToWidget(const nsRect& aGlobalRect, nsRect& aLocalRect) { aLocalRect = aGlobalRect; nsIWidget* theParent = this->GetParent(); if ( theParent ) { // Recursive case // // Convert the local rect to global, except for this level. theParent->WidgetToScreen(aGlobalRect, aLocalRect); NS_RELEASE(theParent); // the offset from our parent is in the x/y of our bounding rect nsRect myBounds; GetBounds(myBounds); aLocalRect.MoveBy(myBounds.x, myBounds.y); } else { // Base case of recursion // // When there is no parent, we're at the top level window. Use // the origin (shifted into local coordinates) to find the offset. StPortSetter portSetter(mWindowPtr); ::SetOrigin(0,0); // convert origin into local coords and shift output rect by that ammount Point origin = {0, 0}; ::GlobalToLocal ( &origin ); aLocalRect.MoveBy ( origin.h, origin.v ); } return NS_OK; } /* * Set a Mac Rect to the value of an nsRect * The source rect is assumed to be in pixels not TWIPS * @update gpk 08/27/98 * @param aRect -- The nsRect that is the source * @param aMacRect -- The Mac Rect destination */ void nsWindow::nsRectToMacRect(const nsRect& aRect, Rect& aMacRect) const { aMacRect.left = aRect.x; aMacRect.top = aRect.y; aMacRect.right = aRect.x + aRect.width; aMacRect.bottom = aRect.y + aRect.height; } //================================================================= /* Convert the coordinates to some device coordinates so GFX can draw. * @update dc 09/16/98 * @param nscoord -- X coordinate to convert * @param nscoord -- Y coordinate to convert * @return NONE */ void nsWindow::ConvertToDeviceCoordinates(nscoord &aX, nscoord &aY) { PRInt32 offX, offY; this->CalcOffset(offX,offY); aX += offX; aY += offY; } NS_IMETHODIMP nsWindow::CaptureRollupEvents(nsIRollupListener * aListener, PRBool aDoCapture, PRBool aConsumeRollupEvent) { if (aDoCapture) { NS_IF_RELEASE(gRollupListener); NS_IF_RELEASE(gRollupWidget); gRollupListener = aListener; NS_ADDREF(aListener); gRollupWidget = this; NS_ADDREF(this); } else { NS_IF_RELEASE(gRollupListener); //gRollupListener = nsnull; NS_IF_RELEASE(gRollupWidget); } return NS_OK; } NS_IMETHODIMP nsWindow::SetTitle(const nsString& title) { NS_ASSERTION(0, "Would some Mac person please implement me? Thanks."); return NS_OK; } NS_IMETHODIMP nsWindow::GetAttention() { // Since the Mac doesn't consider each window a seperate process this call functions // slightly different than on other platforms. We first check to see if we're the // foreground process and, if so, ignore the call. We also check to see if a notification // is already pending and, if so, remove it since we only have one notification per process. // After all that checking we install a notification manager request to mark the app's icon // in the process menu and play the default alert sound OSErr err; if (we_are_front_process()) return NS_OK; if (gNotificationInstalled) { (void)::NMRemove(&gNMRec); gNotificationInstalled = false; } err = GetIconSuite( &gNMRec.nmIcon, 128, svAllSmallData ); if ( err != noErr ) gNMRec.nmIcon = NULL; // Setup and install the notification manager rec gNMRec.qType = nmType; gNMRec.nmMark = 1; // Flag the icon in the process menu gNMRec.nmSound = (Handle)-1L; // Use the default alert sound gNMRec.nmStr = NULL; // No alert/window so no text gNMRec.nmResp = NULL; // No response proc, use the default behavior gNMRec.nmRefCon = NULL; if (::NMInstall(&gNMRec) == noErr) gNotificationInstalled = true; return NS_OK; } #pragma mark - NS_IMETHODIMP nsWindow::ResetInputState() { // currently, the nsMacEventHandler is owned by nsMacWindow, which is the top level window // we deletgate this call to it's parent nsCOMPtr parent = getter_AddRefs(GetParent()); NS_ASSERTION(parent, "cannot get parent"); if(parent) { nsCOMPtr kb = do_QueryInterface(parent); NS_ASSERTION(kb, "cannot get parent"); if(kb) { return kb->ResetInputState(); } } return NS_ERROR_ABORT; } #if !TARGET_CARBON void EachRegionRect (RgnHandle r, void (* proc)(Rect *, void *), void* data) ; // // Written by Hugh Fisher, March 1993. // Used w/out asking his permission. // // This can go away when we get an api on nsIRegion to create one from // a RgnHandle. Then we can use nsIRegion::GetRects to do the work for us. // void EachRegionRect (RgnHandle r, void (* proc)(Rect *, void *), void* inData) { #define EndMark 32767 #define MaxY 32767 #define StackMax 1024 typedef struct { short size; Rect bbox; short data[]; } ** Internal; Internal region; short width, xAdjust, y, index, x1, x2, x; Rect box; short stackStorage[1024]; short * buffer; region = (Internal)r; /* Check for plain rectangle */ if ((**region).size == 10) { proc(&(**region).bbox, inData); return; } /* Got to scale x coordinates into range 0..something */ xAdjust = (**region).bbox.left; width = (**region).bbox.right - xAdjust; /* Most regions will be less than 1024 pixels wide */ if (width < StackMax) buffer = stackStorage; else { buffer = (short *)PR_Malloc(width * 2); if (buffer == NULL) /* Truly humungous region or very low on memory. Quietly doing nothing seems to be the traditional Quickdraw response. */ return; } /* Initialise scan line list to bottom edges */ for (x = (**region).bbox.left; x < (**region).bbox.right; x++) buffer[x - xAdjust] = MaxY; index = 0; /* Loop until we hit an empty scan line */ while ((**region).data[index] != EndMark) { y = (**region).data[index]; index ++; /* Loop through horizontal runs on this line */ while ((**region).data[index] != EndMark) { x1 = (**region).data[index]; index ++; x2 = (**region).data[index]; index ++; x = x1; while (x < x2) { if (buffer[x - xAdjust] < y) { /* We have a bottom edge - how long for? */ box.left = x; box.top = buffer[x - xAdjust]; while (x < x2 && buffer[x - xAdjust] == box.top) { buffer[x - xAdjust] = MaxY; x ++; } /* Pass to client proc */ box.right = x; box.bottom = y; proc(&box, inData); } else { /* This becomes a top edge */ buffer[x - xAdjust] = y; x ++; } } } index ++; } /* Clean up after ourselves */ if (width >= StackMax) PR_Free((void *)buffer); #undef EndMark #undef MaxY #undef StackMax } #endif