/* -*- 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): */ #include "nsViewManager.h" #include "nsUnitConversion.h" #include "nsIRenderingContext.h" #include "nsIDeviceContext.h" #include "nsGfxCIID.h" #include "nsIScrollableView.h" #include "nsView.h" #include "nsIScrollbar.h" #include "nsIClipView.h" #include "nsISupportsArray.h" #include "nsICompositeListener.h" #include "nsCOMPtr.h" static NS_DEFINE_IID(kBlenderCID, NS_BLENDER_CID); static NS_DEFINE_IID(kRegionCID, NS_REGION_CID); static NS_DEFINE_IID(kRenderingContextCID, NS_RENDERING_CONTEXT_CID); //#define GS_DEBUG #define UPDATE_QUANTUM 1000 / 40 //#define NO_DOUBLE_BUFFER #define NEW_COMPOSITOR #define USE_DISPLAY_LIST_ELEMENTS //used for debugging new compositor //#define SHOW_RECTS //#define SHOW_DISPLAYLIST //display list flags #define RENDER_VIEW 0x0000 #define VIEW_INCLUDED 0x0001 #define PUSH_CLIP 0x0002 #define POP_CLIP 0x0004 #define DISPLAYLIST_INC 1 //display list elements struct DisplayListElement { nsIView* mView; nsRect mClip; PRUint32 mFlags; }; static void vm_timer_callback(nsITimer *aTimer, void *aClosure) { nsViewManager *vm = (nsViewManager *)aClosure; //restart the timer if (vm->mTrueFrameRate == vm->mFrameRate) { PRUint32 fr = vm->mFrameRate; vm->mFrameRate = 0; vm->SetFrameRate(fr); } //printf("timer composite...\n"); #ifndef XP_MAC //XXX temporary: The Mac doesn't need the timer to repaint but // obviously this is not the good method to disable the thing. // It's that way for now because the proper solutions // (set UPDATE_QUANTUM to 0, or simply not create the timer) // don't work for now. We'll fix that and then disable the // Mac timers as we should. vm->Composite(); #endif } #if 0 static void blinkRect(nsIRenderingContext* context, const nsRect& r) { context->InvertRect(r); ::PR_Sleep(::PR_MillisecondsToInterval(100)); context->InvertRect(r); } #endif PRUint32 nsViewManager::mVMCount = 0; nsDrawingSurface nsViewManager::mDrawingSurface = nsnull; nsRect nsViewManager::mDSBounds = nsRect(0, 0, 0, 0); nsDrawingSurface nsViewManager::gOffScreen = nsnull; nsDrawingSurface nsViewManager::gRed = nsnull; nsDrawingSurface nsViewManager::gBlue = nsnull; PRInt32 nsViewManager::gBlendWidth = 0; PRInt32 nsViewManager::gBlendHeight = 0; static NS_DEFINE_IID(knsViewManagerIID, NS_IVIEWMANAGER_IID); nsViewManager :: nsViewManager() { NS_INIT_REFCNT(); mVMCount++; mUpdateBatchCnt = 0; mCompositeListeners = nsnull; } nsViewManager :: ~nsViewManager() { if (nsnull != mTimer) { mTimer->Cancel(); //XXX this should not be necessary. MMP NS_RELEASE(mTimer); } NS_IF_RELEASE(mRootWindow); mRootScrollable = nsnull; NS_ASSERTION((mVMCount > 0), "underflow of viewmanagers"); --mVMCount; if ((0 == mVMCount) && ((nsnull != mDrawingSurface) || (nsnull != gOffScreen) || (nsnull != gRed) || (nsnull != gBlue))) { nsIRenderingContext *rc; nsresult rv = nsComponentManager::CreateInstance(kRenderingContextCID, nsnull, NS_GET_IID(nsIRenderingContext), (void **)&rc); if (NS_OK == rv) { if (nsnull != mDrawingSurface) rc->DestroyDrawingSurface(mDrawingSurface); if (nsnull != gOffScreen) rc->DestroyDrawingSurface(gOffScreen); if (nsnull != gRed) rc->DestroyDrawingSurface(gRed); if (nsnull != gBlue) rc->DestroyDrawingSurface(gBlue); NS_RELEASE(rc); } mDrawingSurface = nsnull; gOffScreen = nsnull; gRed = nsnull; gBlue = nsnull; gBlendWidth = 0; gBlendHeight = 0; } mObserver = nsnull; mContext = nsnull; if (nsnull != mDisplayList) { PRInt32 count = mDisplayList->Count(); for (PRInt32 index = 0; index < count; index++) { DisplayListElement* element = (DisplayListElement*) mDisplayList->ElementAt(index); if (element != nsnull) delete element; } delete mDisplayList; mDisplayList = nsnull; } if (nsnull != mTransRgn) { if (nsnull != mTransRects) mTransRgn->FreeRects(mTransRects); NS_RELEASE(mTransRgn); } NS_IF_RELEASE(mBlender); NS_IF_RELEASE(mOpaqueRgn); NS_IF_RELEASE(mTRgn); NS_IF_RELEASE(mRCRgn); NS_IF_RELEASE(mOffScreenCX); NS_IF_RELEASE(mRedCX); NS_IF_RELEASE(mBlueCX); if (nsnull != mCompositeListeners) { mCompositeListeners->Clear(); NS_RELEASE(mCompositeListeners); } } NS_IMPL_QUERY_INTERFACE(nsViewManager, knsViewManagerIID) NS_IMPL_ADDREF(nsViewManager); nsrefcnt nsViewManager::Release(void) { /* Note funny ordering of use of mRefCnt. We were seeing a problem during the deletion of a view hierarchy where child views, while being destroyed, referenced this view manager and caused the Destroy part of this method to be re-entered. Waiting until the destruction has finished to decrement the refcount prevents that. */ NS_LOG_RELEASE(this, mRefCnt - 1, "nsViewManager"); if (mRefCnt == 1) { if (nsnull != mRootView) { // Destroy any remaining views mRootView->Destroy(); mRootView = nsnull; } delete this; return 0; } mRefCnt--; return mRefCnt; } static nsresult CreateRegion(nsIComponentManager* componentManager, nsIRegion* *result) { *result = nsnull; nsIRegion* region = nsnull; nsresult rv = componentManager->CreateInstance(kRegionCID, nsnull, NS_GET_IID(nsIRegion), (void**)®ion); if (NS_SUCCEEDED(rv)) { rv = region->Init(); *result = region; } return rv; } // We don't hold a reference to the presentation context because it // holds a reference to us. NS_IMETHODIMP nsViewManager :: Init(nsIDeviceContext* aContext) { nsresult rv; NS_PRECONDITION(nsnull != aContext, "null ptr"); if (nsnull == aContext) { return NS_ERROR_NULL_POINTER; } if (nsnull != mContext) { return NS_ERROR_ALREADY_INITIALIZED; } mContext = aContext; mDSBounds.Empty(); mTimer = nsnull; mFrameRate = 0; mTrueFrameRate = 0; mTransCnt = 0; rv = SetFrameRate(UPDATE_QUANTUM); mLastRefresh = PR_IntervalNow(); mRefreshEnabled = PR_TRUE; mMouseGrabber = nsnull; mKeyGrabber = nsnull; // create regions nsIComponentManager* componentManager = nsnull; rv = NS_GetGlobalComponentManager(&componentManager); if (NS_SUCCEEDED(rv)) { rv = CreateRegion(componentManager, &mTransRgn); rv = CreateRegion(componentManager, &mOpaqueRgn); rv = CreateRegion(componentManager, &mTRgn); rv = CreateRegion(componentManager, &mRCRgn); } return rv; } NS_IMETHODIMP nsViewManager :: GetRootView(nsIView *&aView) { aView = mRootView; return NS_OK; } NS_IMETHODIMP nsViewManager :: SetRootView(nsIView *aView) { UpdateTransCnt(mRootView, aView); // Do NOT destroy the current root view. It's the caller's responsibility // to destroy it mRootView = aView; //now get the window too. NS_IF_RELEASE(mRootWindow); if (nsnull != mRootView) mRootView->GetWidget(mRootWindow); return NS_OK; } NS_IMETHODIMP nsViewManager :: GetFrameRate(PRUint32 &aRate) { aRate = mTrueFrameRate; return NS_OK; } NS_IMETHODIMP nsViewManager :: SetFrameRate(PRUint32 aFrameRate) { nsresult rv; if (aFrameRate != mFrameRate) { if (nsnull != mTimer) { mTimer->Cancel(); //XXX this should not be necessary. MMP NS_RELEASE(mTimer); } mFrameRate = aFrameRate; mTrueFrameRate = aFrameRate; if (mFrameRate != 0) { rv = NS_NewTimer(&mTimer); if (NS_OK == rv) mTimer->Init(vm_timer_callback, this, 1000 / mFrameRate); } else rv = NS_OK; } else rv = NS_OK; return rv; } NS_IMETHODIMP nsViewManager :: GetWindowDimensions(nscoord *width, nscoord *height) { if (nsnull != mRootView) mRootView->GetDimensions(width, height); else { *width = 0; *height = 0; } return NS_OK; } NS_IMETHODIMP nsViewManager :: SetWindowDimensions(nscoord width, nscoord height) { // Resize the root view if (nsnull != mRootView) mRootView->SetDimensions(width, height); //printf("new dims: %d %d\n", width, height); // Inform the presentation shell that we've been resized if (nsnull != mObserver) mObserver->ResizeReflow(mRootView, width, height); //printf("reflow done\n"); return NS_OK; } NS_IMETHODIMP nsViewManager :: ResetScrolling(void) { if (nsnull != mRootScrollable) mRootScrollable->ComputeScrollOffsets(PR_TRUE); return NS_OK; } void nsViewManager :: Refresh(nsIView *aView, nsIRenderingContext *aContext, nsIRegion *region, PRUint32 aUpdateFlags) { nsRect wrect; nsIRenderingContext *localcx = nsnull; nsDrawingSurface ds = nsnull; if (PR_FALSE == mRefreshEnabled) return; NS_ASSERTION(!(PR_TRUE == mPainting), "recursive painting not permitted"); mPainting = PR_TRUE; //printf("refreshing region...\n"); //force double buffering because of non-opaque views? if (mTransCnt > 0) aUpdateFlags |= NS_VMREFRESH_DOUBLE_BUFFER; #ifdef NO_DOUBLE_BUFFER aUpdateFlags &= ~NS_VMREFRESH_DOUBLE_BUFFER; #endif if (nsnull == aContext) { localcx = CreateRenderingContext(*aView); //couldn't get rendering context. this is ok at init time atleast if (nsnull == localcx) return; } else localcx = aContext; // notify the listeners. if (nsnull != mCompositeListeners) { PRUint32 listenerCount; if (NS_SUCCEEDED(mCompositeListeners->Count(&listenerCount))) { nsICompositeListener* listener; for (PRUint32 i = 0; i < listenerCount; i++) { if (NS_SUCCEEDED(mCompositeListeners->QueryElementAt(i, NS_GET_IID(nsICompositeListener), (void**)&listener))) { listener->WillRefreshRegion(this, aView, aContext, region, aUpdateFlags); NS_RELEASE(listener); } } } } if (aUpdateFlags & NS_VMREFRESH_DOUBLE_BUFFER) { nsIWidget* widget; aView->GetWidget(widget); widget->GetClientBounds(wrect); wrect.x = wrect.y = 0; NS_RELEASE(widget); ds = GetDrawingSurface(*localcx, wrect); } PRBool result; nsRect trect; if (nsnull != region) localcx->SetClipRegion(*region, nsClipCombine_kUnion, result); aView->GetBounds(trect); localcx->SetClipRect(trect, nsClipCombine_kIntersect, result); RenderViews(aView, *localcx, trect, result); if ((aUpdateFlags & NS_VMREFRESH_DOUBLE_BUFFER) && ds) #ifdef NEW_COMPOSITOR localcx->CopyOffScreenBits(ds, wrect.x, wrect.y, wrect, 0); #else localcx->CopyOffScreenBits(ds, wrect.x, wrect.y, wrect, NS_COPYBITS_USE_SOURCE_CLIP_REGION); #endif if (localcx != aContext) NS_RELEASE(localcx); // Subtract the area we just painted from the dirty region if ((nsnull != region) && !region->IsEmpty()) { nsRect pixrect = trect; float t2p; mContext->GetAppUnitsToDevUnits(t2p); pixrect.ScaleRoundIn(t2p); region->Subtract(pixrect.x, pixrect.y, pixrect.width, pixrect.height); } mLastRefresh = PR_IntervalNow(); mPainting = PR_FALSE; // notify the listeners. if (nsnull != mCompositeListeners) { PRUint32 listenerCount; if (NS_SUCCEEDED(mCompositeListeners->Count(&listenerCount))) { nsICompositeListener* listener; for (PRUint32 i = 0; i < listenerCount; i++) { if (NS_SUCCEEDED(mCompositeListeners->QueryElementAt(i, NS_GET_IID(nsICompositeListener), (void**)&listener))) { listener->DidRefreshRegion(this, aView, aContext, region, aUpdateFlags); NS_RELEASE(listener); } } } } } void nsViewManager :: Refresh(nsIView *aView, nsIRenderingContext *aContext, const nsRect *rect, PRUint32 aUpdateFlags) { nsRect wrect, brect; nsIRenderingContext *localcx = nsnull; nsDrawingSurface ds = nsnull; if (PR_FALSE == mRefreshEnabled) return; NS_ASSERTION(!(PR_TRUE == mPainting), "recursive painting not permitted"); mPainting = PR_TRUE; //force double buffering because of non-opaque views? //printf("refreshing rect... "); //stdout << *rect; //printf("\n"); if (mTransCnt > 0) aUpdateFlags |= NS_VMREFRESH_DOUBLE_BUFFER; #ifdef NO_DOUBLE_BUFFER aUpdateFlags &= ~NS_VMREFRESH_DOUBLE_BUFFER; #endif if (nsnull == aContext) { localcx = CreateRenderingContext(*aView); //couldn't get rendering context. this is ok if at startup if (nsnull == localcx) return; } else localcx = aContext; // notify the listeners. if (nsnull != mCompositeListeners) { PRUint32 listenerCount; if (NS_SUCCEEDED(mCompositeListeners->Count(&listenerCount))) { nsICompositeListener* listener; for (PRUint32 i = 0; i < listenerCount; i++) { if (NS_SUCCEEDED(mCompositeListeners->QueryElementAt(i, NS_GET_IID(nsICompositeListener), (void**)&listener))) { listener->WillRefreshRect(this, aView, aContext, rect, aUpdateFlags); NS_RELEASE(listener); } } } } if (aUpdateFlags & NS_VMREFRESH_DOUBLE_BUFFER) { nsIWidget* widget; aView->GetWidget(widget); widget->GetClientBounds(wrect); brect = wrect; wrect.x = wrect.y = 0; NS_RELEASE(widget); ds = GetDrawingSurface(*localcx, wrect); } nsRect trect = *rect; PRBool result; localcx->SetClipRect(trect, nsClipCombine_kReplace, result); RenderViews(aView, *localcx, trect, result); if ((aUpdateFlags & NS_VMREFRESH_DOUBLE_BUFFER) && ds) #ifdef NEW_COMPOSITOR { #ifdef XP_MAC // localcx->SelectOffScreenDrawingSurface(nsnull); // blinkRect(localcx, trect); localcx->CopyOffScreenBits(ds, wrect.x, wrect.y, wrect, 0); #else #ifdef XP_UNIX localcx->SetClipRect(trect, nsClipCombine_kReplace, result); #endif //unix localcx->CopyOffScreenBits(ds, brect.x, brect.y, brect, 0); #endif //mac } #else localcx->CopyOffScreenBits(ds, wrect.x, wrect.y, wrect, NS_COPYBITS_USE_SOURCE_CLIP_REGION); #endif if (localcx != aContext) NS_RELEASE(localcx); #if 0 // Subtract the area we just painted from the dirty region nsIRegion *dirtyRegion; aView->GetDirtyRegion(dirtyRegion); if ((nsnull != dirtyRegion) && !dirtyRegion->IsEmpty()) { nsRect pixrect = trect; float t2p; mContext->GetAppUnitsToDevUnits(t2p); pixrect.ScaleRoundIn(t2p); dirtyRegion->Subtract(pixrect.x, pixrect.y, pixrect.width, pixrect.height); NS_RELEASE(dirtyRegion); } #endif mLastRefresh = PR_IntervalNow(); mPainting = PR_FALSE; // notify the listeners. if (nsnull != mCompositeListeners) { PRUint32 listenerCount; if (NS_SUCCEEDED(mCompositeListeners->Count(&listenerCount))) { nsICompositeListener* listener; for (PRUint32 i = 0; i < listenerCount; i++) { if (NS_SUCCEEDED(mCompositeListeners->QueryElementAt(i, NS_GET_IID(nsICompositeListener), (void**)&listener))) { listener->DidRefreshRect(this, aView, aContext, rect, aUpdateFlags); NS_RELEASE(listener); } } } } } //states typedef enum { FRONT_TO_BACK_RENDER = 1, FRONT_TO_BACK_ACCUMULATE, BACK_TO_FRONT_TRANS, BACK_TO_FRONT_OPACITY, FRONT_TO_BACK_CLEANUP, FRONT_TO_BACK_POP_SEARCH, COMPOSITION_DONE } nsCompState; //bit shifts #define TRANS_PROPERTY_TRANS 0 #define TRANS_PROPERTY_OPACITY 1 #ifdef SHOW_RECTS static PRInt32 evenodd = 0; #endif void nsViewManager :: RenderViews(nsIView *aRootView, nsIRenderingContext& aRC, const nsRect& aRect, PRBool &aResult) { #ifdef NEW_COMPOSITOR #define SET_STATE(x) { prevstate = state; state = (x); } PRInt32 flatlen = 0, cnt; nsCompState state = FRONT_TO_BACK_RENDER, prevstate; PRInt32 transprop = 0; PRInt32 increment = DISPLAYLIST_INC; PRInt32 loopstart = 0, backstart; PRInt32 loopend; PRInt32 accumstart; float t2p, p2t; PRInt32 rgnrect; nsRect localrect, trect; PRBool useopaque = PR_FALSE; nsRegionRectSet onerect, *rectset; nsCOMPtr paintedRgn; if (NS_FAILED(nsComponentManager::CreateInstance(kRegionCID, nsnull, NS_GET_IID(nsIRegion), (void**) getter_AddRefs(paintedRgn)))) return; paintedRgn->Init(); //printf("-------------begin paint------------\n"); if (aRootView && mRootView) { nscoord ox = 0, oy = 0; ComputeViewOffset(aRootView, &ox, &oy, 1); CreateDisplayList(mRootView, &flatlen, ox, oy, aRootView, &aRect); } loopend = flatlen; mContext->GetAppUnitsToDevUnits(t2p); mContext->GetDevUnitsToAppUnits(p2t); #ifdef SHOW_DISPLAYLIST ShowDisplayList(flatlen); #endif while (state != COMPOSITION_DONE) { nsIView *curview; nsRect *currect; PRUint32 curflags; PRBool pushing; PRUint32 pushcnt = 0; PRBool clipstate; for (cnt = loopstart; (increment > 0) ? (cnt < loopend) : (cnt > loopend); cnt += increment) { DisplayListElement* curelement = (DisplayListElement*) mDisplayList->ElementAt(cnt); if (nsnull == curelement) continue; curview = curelement->mView; currect = &curelement->mClip; curflags = curelement->mFlags; if ((nsnull != curview) && (nsnull != currect)) { PRBool hasWidget = DoesViewHaveNativeWidget(*curview); PRBool isBottom = cnt == (flatlen - DISPLAYLIST_INC); if (curflags & PUSH_CLIP) { if (state == BACK_TO_FRONT_OPACITY) { mOffScreenCX->PopState(clipstate); mRedCX->PopState(clipstate); mBlueCX->PopState(clipstate); // permanently remove any painted opaque views. mOffScreenCX->SetClipRegion(*paintedRgn, nsClipCombine_kSubtract, clipstate); mRedCX->SetClipRegion(*paintedRgn, nsClipCombine_kSubtract, clipstate); mRedCX->SetClipRegion(*paintedRgn, nsClipCombine_kSubtract, clipstate); pushcnt--; NS_ASSERTION(!((PRInt32)pushcnt < 0), "underflow"); } else if (state == BACK_TO_FRONT_TRANS) { aRC.PopState(clipstate); pushcnt--; NS_ASSERTION(!((PRInt32)pushcnt < 0), "underflow"); // permanently remove any painted opaque views. aRC.SetClipRegion(*paintedRgn, nsClipCombine_kSubtract, clipstate); } else { pushing = FRONT_TO_BACK_RENDER; aRC.PushState(); aRC.SetClipRect(*currect, nsClipCombine_kIntersect, clipstate); pushcnt++; } } else if (curflags & POP_CLIP) { if (state == BACK_TO_FRONT_OPACITY) { pushing = BACK_TO_FRONT_OPACITY; mOffScreenCX->PushState(); mRedCX->PushState(); mBlueCX->PushState(); mOffScreenCX->SetClipRect(*currect, nsClipCombine_kIntersect, clipstate); mRedCX->SetClipRect(*currect, nsClipCombine_kIntersect, clipstate); mBlueCX->SetClipRect(*currect, nsClipCombine_kIntersect, clipstate); pushcnt++; } else if (state == BACK_TO_FRONT_TRANS) { pushing = FRONT_TO_BACK_RENDER; aRC.PushState(); aRC.SetClipRect(*currect, nsClipCombine_kIntersect, clipstate); pushcnt++; } else { aRC.PopState(clipstate); pushcnt--; NS_ASSERTION(!((PRInt32)pushcnt < 0), "underflow"); // permanently remove any painted opaque views. aRC.SetClipRegion(*paintedRgn, nsClipCombine_kSubtract, clipstate); } if (state == FRONT_TO_BACK_POP_SEARCH) { aRC.SetClipRect(*currect, nsClipCombine_kSubtract, clipstate); if (!clipstate) state = prevstate; else if (pushcnt == 0) aResult = clipstate; } } else // if (isBottom) // if (!hasWidget || isBottom) { nsPoint *point; curview->GetScratchPoint(&point); if (!hasWidget || (hasWidget && point->x)) { PRBool trans = PR_FALSE; float opacity = 1.0f; PRBool translucent = PR_FALSE; if (!isBottom) { curview->HasTransparency(trans); curview->GetOpacity(opacity); translucent = opacity < 1.0f; } switch (state) { case FRONT_TO_BACK_RENDER: if (trans || translucent) { if (mTransRgn && mOpaqueRgn) { //need to finish using back to front till this point SET_STATE(FRONT_TO_BACK_ACCUMULATE) if (pushcnt == 0) accumstart = cnt; else accumstart = 0; mTransRgn->SetTo(0, 0, 0, 0); mOpaqueRgn->SetTo(0, 0, 0, 0); } //falls through } else { RenderView(curview, aRC, aRect, *currect, aResult); // accumulate region of all rendered opaque views. trect.IntersectRect(*currect, aRect), trect *= t2p; paintedRgn->Union(trect.x, trect.y, trect.width, trect.height); if (aResult == PR_FALSE) { aRC.SetClipRect(*currect, nsClipCombine_kSubtract, clipstate); if (clipstate) { if (pushcnt > 0) SET_STATE(FRONT_TO_BACK_POP_SEARCH) else aResult = clipstate; } } break; } case FRONT_TO_BACK_ACCUMULATE: { trect.IntersectRect(*currect, aRect); trect *= t2p; if (trans || translucent || mTransRgn->ContainsRect(trect.x, trect.y, trect.width, trect.height) || mOpaqueRgn->ContainsRect(trect.x, trect.y, trect.width, trect.height)) { transprop |= (trans << TRANS_PROPERTY_TRANS) | (translucent << TRANS_PROPERTY_OPACITY); curelement->mFlags = (VIEW_INCLUDED | curflags); if (!isBottom) { //printf("adding %d %d %d %d\n", trect.x, trect.y, trect.width, trect.height); if (trans || translucent) mTransRgn->Union(trect.x, trect.y, trect.width, trect.height); else mOpaqueRgn->Union(trect.x, trect.y, trect.width, trect.height); } } else { RenderView(curview, aRC, aRect, *currect, aResult); // accumulate region of all rendered opaque views. trect.IntersectRect(*currect, aRect), trect *= t2p; paintedRgn->Union(trect.x, trect.y, trect.width, trect.height); if (aResult == PR_FALSE) { aRC.SetClipRect(*currect, nsClipCombine_kSubtract, clipstate); if (clipstate) { if (pushcnt > 0) SET_STATE(FRONT_TO_BACK_POP_SEARCH) else aResult = clipstate; } } } break; } case FRONT_TO_BACK_POP_SEARCH: break; case BACK_TO_FRONT_TRANS: if ((curflags & VIEW_INCLUDED) && localrect.Intersects(*currect)) { RenderView(curview, aRC, localrect, *currect, clipstate); } break; case FRONT_TO_BACK_CLEANUP: if ((curflags & VIEW_INCLUDED) && !trans && !translucent && localrect.Intersects(*currect)) { RenderView(curview, aRC, localrect, *currect, aResult); if (aResult == PR_FALSE) { aRC.SetClipRect(*currect, nsClipCombine_kSubtract, clipstate); if (clipstate) { if (pushcnt > 0) SET_STATE(FRONT_TO_BACK_POP_SEARCH) else aResult = clipstate; } } } break; case BACK_TO_FRONT_OPACITY: if (curflags & VIEW_INCLUDED) { nsRect blendrect, pixrect; if (blendrect.IntersectRect(*currect, localrect)) { pixrect = blendrect; pixrect.x -= localrect.x; pixrect.y -= localrect.y; pixrect *= t2p; //if there is nothing to render in terms of pixels, //just bag it right here. if ((pixrect.width == 0) || (pixrect.height == 0)) break; if (!translucent) RenderView(curview, *mOffScreenCX, localrect, *currect, clipstate); else { if (trans) { mRedCX->SetColor(NS_RGB(255, 0, 0)); mRedCX->FillRect(blendrect); #ifdef SHOW_RECTS mRedCX->SetColor(NS_RGB(255 - evenodd, evenodd, 255 - evenodd)); mRedCX->DrawLine(blendrect.x, blendrect.y, blendrect.x + blendrect.width, blendrect.y + blendrect.height); #endif } RenderView(curview, *mRedCX, localrect, *currect, clipstate); if (trans) { mBlueCX->SetColor(NS_RGB(0, 0, 255)); mBlueCX->FillRect(blendrect); #ifdef SHOW_RECTS mBlueCX->SetColor(NS_RGB(255 - evenodd, evenodd, 255 - evenodd)); mBlueCX->DrawLine(blendrect.x, blendrect.y, blendrect.x + blendrect.width, blendrect.y + blendrect.height); #endif RenderView(curview, *mBlueCX, localrect, *currect, clipstate); } if (nsnull == mBlender) { if (NS_OK == nsComponentManager::CreateInstance(kBlenderCID, nsnull, NS_GET_IID(nsIBlender), (void **)&mBlender)) mBlender->Init(mContext); } if (nsnull != mBlender) mBlender->Blend(pixrect.x, pixrect.y, pixrect.width, pixrect.height, mRedCX, mOffScreenCX, pixrect.x, pixrect.y, opacity, trans ? mBlueCX : nsnull, NS_RGB(255, 0, 0), NS_RGB(0, 0, 255)); } } } break; default: break; } } else { if (state == BACK_TO_FRONT_OPACITY) { mOffScreenCX->SetClipRect(*currect, nsClipCombine_kSubtract, clipstate); mRedCX->SetClipRect(*currect, nsClipCombine_kSubtract, clipstate); mBlueCX->SetClipRect(*currect, nsClipCombine_kSubtract, clipstate); } else aRC.SetClipRect(*currect, nsClipCombine_kSubtract, aResult); } } if (aResult == PR_TRUE) break; } } NS_ASSERTION(!((PRInt32)pushcnt < 0), "underflow"); while (pushcnt--) { NS_ASSERTION(!((PRInt32)pushcnt < 0), "underflow"); if (pushing == BACK_TO_FRONT_OPACITY) { mOffScreenCX->PopState(clipstate); mRedCX->PopState(clipstate); mBlueCX->PopState(clipstate); // permanently remove any painted opaque views. mOffScreenCX->SetClipRegion(*paintedRgn, nsClipCombine_kSubtract, clipstate); mRedCX->SetClipRegion(*paintedRgn, nsClipCombine_kSubtract, clipstate); mBlueCX->SetClipRegion(*paintedRgn, nsClipCombine_kSubtract, clipstate); } else { aRC.PopState(clipstate); // permanently remove any painted opaque views. aRC.SetClipRegion(*paintedRgn, nsClipCombine_kSubtract, clipstate); } } switch (state) { case FRONT_TO_BACK_ACCUMULATE: SET_STATE((transprop & (1 << TRANS_PROPERTY_OPACITY)) ? BACK_TO_FRONT_OPACITY : BACK_TO_FRONT_TRANS) if ((curflags & (POP_CLIP | PUSH_CLIP)) || (state == BACK_TO_FRONT_OPACITY)) backstart = flatlen - DISPLAYLIST_INC; else { backstart = flatlen - (DISPLAYLIST_INC << 1); //render the bottom most view RenderView(curview, aRC, aRect, *currect, aResult); } //get a snapshot of the current clip so that we can exclude areas //already excluded in it from the transparency region aRC.GetClipRegion(&mRCRgn); if (!mOpaqueRgn->IsEmpty()) useopaque = PR_TRUE; if (mTRgn) { PRInt32 x, y, w, h; mTransRgn->GetBoundingBox(&x, &y, &w, &h); mTRgn->SetTo(x, y, w, h); mTRgn->Subtract(*mRCRgn); mTransRgn->Subtract(*mTRgn); mTransRgn->GetBoundingBox(&x, &y, &w, &h); // permanently remove any opaque painted views. mTransRgn->Subtract(*paintedRgn); aRC.SetClipRegion(*mTransRgn, nsClipCombine_kReplace, aResult); //did that finish everything? if (aResult == PR_TRUE) { SET_STATE(COMPOSITION_DONE) continue; } mTransRgn->GetRects(&mTransRects); //see if it might be better to ignore the rects and just do everything as one operation... if ((state == BACK_TO_FRONT_TRANS) && (mTransRects->mNumRects > 1) && (mTransRects->mArea >= PRUint32(w * h * 0.9f))) { rectset = &onerect; rectset->mNumRects = 1; rectset->mRects[0].x = x; rectset->mRects[0].y = y; rectset->mRects[0].width = w; rectset->mRects[0].height = h; } else rectset = mTransRects; rgnrect = 0; #ifdef SHOW_RECTS evenodd = 255 - evenodd; #endif } //falls through case BACK_TO_FRONT_TRANS: case BACK_TO_FRONT_OPACITY: if (rgnrect > 0) { if (state == BACK_TO_FRONT_OPACITY) { #ifdef SHOW_RECTS mOffScreenCX->SetColor(NS_RGB(255 - evenodd, evenodd, 255 - evenodd)); mOffScreenCX->DrawRect(localrect); #endif mOffScreenCX->Translate(localrect.x, localrect.y); mRedCX->Translate(localrect.x, localrect.y); mBlueCX->Translate(localrect.x, localrect.y); //buffer swap. aRC.CopyOffScreenBits(gOffScreen, 0, 0, localrect, NS_COPYBITS_XFORM_DEST_VALUES | NS_COPYBITS_TO_BACK_BUFFER); } else { #ifdef SHOW_RECTS aRC.SetColor(NS_RGB(255 - evenodd, evenodd, 255 - evenodd)); aRC.DrawRect(localrect); // for (int xxxxx = 0; xxxxx < 50000000; xxxxx++); #endif } //if this is the last rect, we don't need to subtract it, cause //we're done with this region. if (rgnrect < (PRInt32)rectset->mNumRects) aRC.SetClipRect(localrect, nsClipCombine_kSubtract, aResult); } if (rgnrect < (PRInt32)rectset->mNumRects) { loopstart = backstart; loopend = accumstart - DISPLAYLIST_INC; increment = -DISPLAYLIST_INC; localrect.x = rectset->mRects[rgnrect].x; localrect.y = rectset->mRects[rgnrect].y; localrect.width = rectset->mRects[rgnrect].width; localrect.height = rectset->mRects[rgnrect].height; if (state == BACK_TO_FRONT_OPACITY) { //prepare offscreen buffers if ((localrect.width > gBlendWidth) || (localrect.height > gBlendHeight)) { nsRect bitrect = nsRect(0, 0, localrect.width, localrect.height); NS_IF_RELEASE(mOffScreenCX); NS_IF_RELEASE(mRedCX); NS_IF_RELEASE(mBlueCX); if (nsnull != gOffScreen) { aRC.DestroyDrawingSurface(gOffScreen); gOffScreen = nsnull; } aRC.CreateDrawingSurface(&bitrect, NS_CREATEDRAWINGSURFACE_FOR_PIXEL_ACCESS, gOffScreen); if (nsnull != gRed) { aRC.DestroyDrawingSurface(gRed); gRed = nsnull; } aRC.CreateDrawingSurface(&bitrect, NS_CREATEDRAWINGSURFACE_FOR_PIXEL_ACCESS, gRed); if (nsnull != gBlue) { aRC.DestroyDrawingSurface(gBlue); gBlue = nsnull; } aRC.CreateDrawingSurface(&bitrect, NS_CREATEDRAWINGSURFACE_FOR_PIXEL_ACCESS, gBlue); gBlendWidth = localrect.width; gBlendHeight = localrect.height; //printf("offscr: %d, %d (%d, %d)\n", w, h, accumrect.width, accumrect.height); } // bug 5062: recreate the local blending contexts if necessary, since global drawing surfaces may have // been created while viewing another page, have to make sure local contexts exist. if (mOffScreenCX == NULL) { if (NS_OK == nsComponentManager::CreateInstance(kRenderingContextCID, nsnull, NS_GET_IID(nsIRenderingContext), (void **)&mOffScreenCX)) mOffScreenCX->Init(mContext, gOffScreen); } if (mRedCX == NULL) { if (NS_OK == nsComponentManager::CreateInstance(kRenderingContextCID, nsnull, NS_GET_IID(nsIRenderingContext), (void **)&mRedCX)) mRedCX->Init(mContext, gRed); } if (mBlueCX == NULL) { if (NS_OK == nsComponentManager::CreateInstance(kRenderingContextCID, nsnull, NS_GET_IID(nsIRenderingContext), (void **)&mBlueCX)) mBlueCX->Init(mContext, gBlue); } if ((nsnull == gOffScreen) || (nsnull == gRed)) SET_STATE(BACK_TO_FRONT_TRANS) } //printf("rect: %d %d %d %d\n", localrect.x, localrect.y, localrect.width, localrect.height); localrect *= p2t; rgnrect++; if (state == BACK_TO_FRONT_OPACITY) { mOffScreenCX->Translate(-localrect.x, -localrect.y); mRedCX->Translate(-localrect.x, -localrect.y); mBlueCX->Translate(-localrect.x, -localrect.y); mOffScreenCX->SetClipRect(localrect, nsClipCombine_kReplace, clipstate); mRedCX->SetClipRect(localrect, nsClipCombine_kReplace, clipstate); mBlueCX->SetClipRect(localrect, nsClipCombine_kReplace, clipstate); } } else { if (useopaque) { mOpaqueRgn->Subtract(*mTransRgn); if (!mOpaqueRgn->IsEmpty()) { SET_STATE(FRONT_TO_BACK_CLEANUP) loopstart = accumstart; loopend = flatlen; increment = DISPLAYLIST_INC; mOpaqueRgn->GetBoundingBox(&localrect.x, &localrect.y, &localrect.width, &localrect.height); localrect *= p2t; // permanently remove any opaque painted views. mOpaqueRgn->Subtract(*paintedRgn); aRC.SetClipRegion(*mOpaqueRgn, nsClipCombine_kReplace, aResult); //did that finish everything? if (aResult == PR_TRUE) SET_STATE(COMPOSITION_DONE) break; } } SET_STATE(COMPOSITION_DONE) } break; default: case FRONT_TO_BACK_CLEANUP: case FRONT_TO_BACK_RENDER: SET_STATE(COMPOSITION_DONE) break; } } if (nsnull != aRootView) ComputeViewOffset(aRootView, nsnull, nsnull, 0); #undef SET_STATE #else // Paint the view. The clipping rect was set above set don't clip again. aRootView->Paint(aRC, aRect, NS_VIEW_FLAG_CLIP_SET, aResult); #endif } void nsViewManager :: RenderView(nsIView *aView, nsIRenderingContext &aRC, const nsRect &aDamageRect, nsRect &aGlobalRect, PRBool &aResult) { nsRect drect; NS_ASSERTION(!(nsnull == aView), "no view"); aRC.PushState(); aRC.Translate(aGlobalRect.x, aGlobalRect.y); drect.IntersectRect(aDamageRect, aGlobalRect); drect.x -= aGlobalRect.x; drect.y -= aGlobalRect.y; aView->Paint(aRC, drect, NS_VIEW_FLAG_JUST_PAINT, aResult); aRC.PopState(aResult); } void nsViewManager::UpdateDirtyViews(nsIView *aView, nsRect *aParentRect) const { nsRect bounds; aView->GetBounds(bounds); // translate parent rect into child coords. nsRect parDamage; if (nsnull != aParentRect) { parDamage = *aParentRect; parDamage.IntersectRect(bounds, parDamage); parDamage.MoveBy(-bounds.x, -bounds.y); } else parDamage = bounds; if (PR_FALSE == parDamage.IsEmpty()) { nsIWidget *widget; aView->GetWidget(widget); if (nsnull != widget) { float scale; nsRect pixrect = parDamage; mContext->GetAppUnitsToDevUnits(scale); pixrect.ScaleRoundOut(scale); //printf("invalidating: view %x (pix) %d, %d\n", aView, pixrect.width, pixrect.height); widget->Invalidate(pixrect, PR_FALSE); NS_RELEASE(widget); } } // Check our child views nsIView *child; aView->GetChild(0, child); while (nsnull != child) { UpdateDirtyViews(child, &parDamage); child->GetNextSibling(child); } } void nsViewManager::ProcessPendingUpdates(nsIView* aView) { PRBool hasWidget; aView->HasWidget(&hasWidget); if (hasWidget) { nsCOMPtr dirtyRegion; aView->GetDirtyRegion(*getter_AddRefs(dirtyRegion)); if (dirtyRegion != nsnull && !dirtyRegion->IsEmpty()) { nsCOMPtr widget; aView->GetWidget(*getter_AddRefs(widget)); if (widget) { widget->InvalidateRegion(dirtyRegion, PR_FALSE); } dirtyRegion->Init(); } } // process pending updates in child view. nsIView* childView = nsnull; aView->GetChild(0, childView); while (nsnull != childView) { ProcessPendingUpdates(childView); childView->GetNextSibling(childView); } } NS_IMETHODIMP nsViewManager :: Composite() { if (mUpdateCnt > 0) { if (nsnull != mRootWindow) mRootWindow->Update(); mUpdateCnt = 0; PauseTimer(); } return NS_OK; } NS_IMETHODIMP nsViewManager::UpdateView(nsIView *aView, PRUint32 aUpdateFlags) { // Mark the entire view as damaged nsRect bounds; aView->GetBounds(bounds); bounds.x = bounds.y = 0; return UpdateView(aView, bounds, aUpdateFlags); } NS_IMETHODIMP nsViewManager::UpdateView(nsIView *aView, const nsRect &aRect, PRUint32 aUpdateFlags) { NS_PRECONDITION(nsnull != aView, "null view"); if (!mRefreshEnabled) { // accumulate this rectangle in the view's dirty region, so we can process it later. if (aRect.width != 0 && aRect.height != 0) { AddRectToDirtyRegion(aView, aRect); ++mUpdateCnt; } return NS_OK; } // Ignore any silly requests... if ((aRect.width == 0) || (aRect.height == 0)) return NS_OK; // is this view even visible? nsViewVisibility visibility; aView->GetVisibility(visibility); if (visibility == nsViewVisibility_kHide) return NS_OK; // Find the nearest view (including this view) that has a widget nsIView *widgetView = GetWidgetView(aView); if (nsnull != widgetView) { if (0 == mUpdateCnt) RestartTimer(); mUpdateCnt++; #if 0 // Transform damaged rect to widgetView's coordinate system. nsRect widgetRect = aRect; nsIView *parentView = aView; while (parentView != widgetView) { nscoord x, y; parentView->GetPosition(&x, &y); widgetRect.x += x; widgetRect.y += y; parentView->GetParent(parentView); } // Add this rect to the widgetView's dirty region. if (nsnull != widgetView) UpdateDirtyViews(widgetView, &widgetRect); #else // Go ahead and invalidate the entire rectangular area. // regardless of parentage. nsRect widgetRect = aRect; ViewToWidget(aView, widgetView, widgetRect); nsCOMPtr widget; widgetView->GetWidget(*getter_AddRefs(widget)); widget->Invalidate(widgetRect, PR_FALSE); #endif // See if we should do an immediate refresh or wait if (aUpdateFlags & NS_VMREFRESH_IMMEDIATE) { Composite(); } else if ((mTrueFrameRate > 0) && !(aUpdateFlags & NS_VMREFRESH_NO_SYNC)) { // or if a sync paint is allowed and it's time for the compositor to // do a refresh PRInt32 deltams = PR_IntervalToMilliseconds(PR_IntervalNow() - mLastRefresh); if (deltams > (1000 / (PRInt32)mTrueFrameRate)) Composite(); } } return NS_OK; } NS_IMETHODIMP nsViewManager::UpdateAllViews(PRUint32 aUpdateFlags) { UpdateViews(mRootView, aUpdateFlags); return NS_OK; } void nsViewManager::UpdateViews(nsIView *aView, PRUint32 aUpdateFlags) { // update this view. UpdateView(aView, aUpdateFlags); // update all children as well. nsIView* childView = nsnull; aView->GetChild(0, childView); while (nsnull != childView) { UpdateViews(childView, aUpdateFlags); childView->GetNextSibling(childView); } } NS_IMETHODIMP nsViewManager :: DispatchEvent(nsGUIEvent *aEvent, nsEventStatus *aStatus) { *aStatus = nsEventStatus_eIgnore; switch(aEvent->message) { case NS_SIZE: { nsIView* view = nsView::GetViewFor(aEvent->widget); if (nsnull != view) { nscoord width = ((nsSizeEvent*)aEvent)->windowSize->width; nscoord height = ((nsSizeEvent*)aEvent)->windowSize->height; width = ((nsSizeEvent*)aEvent)->mWinWidth; height = ((nsSizeEvent*)aEvent)->mWinHeight; // The root view may not be set if this is the resize associated with // window creation if (view == mRootView) { // Convert from pixels to twips float p2t; mContext->GetDevUnitsToAppUnits(p2t); //printf("resize: (pix) %d, %d\n", width, height); SetWindowDimensions(NSIntPixelsToTwips(width, p2t), NSIntPixelsToTwips(height, p2t)); *aStatus = nsEventStatus_eConsumeNoDefault; } } break; } case NS_PAINT: { nsIView *view = nsView::GetViewFor(aEvent->widget); if (nsnull != view) { // The rect is in device units, and it's in the coordinate space of its // associated window. nsRect damrect = *((nsPaintEvent*)aEvent)->rect; float p2t; mContext->GetDevUnitsToAppUnits(p2t); damrect.ScaleRoundOut(p2t); // Do an immediate refresh if (nsnull != mContext) { nsRect viewrect; float varea; // Check that there's actually something to paint view->GetBounds(viewrect); viewrect.x = viewrect.y = 0; varea = (float)viewrect.width * viewrect.height; if (varea > 0.0000001f) { // nsRect arearect; PRUint32 updateFlags = 0; // Auto double buffering logic. // See if the paint region is greater than .25 the area of our view. // If so, enable double buffered painting. // XXX These two lines cause a lot of flicker for drag-over re-drawing - rods //arearect.IntersectRect(damrect, viewrect); //if ((((float)arearect.width * arearect.height) / varea) > 0.25f) // XXX rods updateFlags |= NS_VMREFRESH_DOUBLE_BUFFER; //printf("refreshing: view: %x, %d, %d, %d, %d\n", view, trect.x, trect.y, trect.width, trect.height); // Refresh the view Refresh(view, ((nsPaintEvent*)aEvent)->renderingContext, &damrect, updateFlags); } } *aStatus = nsEventStatus_eConsumeNoDefault; } break; } case NS_DESTROY: *aStatus = nsEventStatus_eConsumeNoDefault; break; default: { nsIView* baseView; nsIView* view; nsPoint offset; nsIScrollbar* sb; //Find the view whose coordinates system we're in. baseView = nsView::GetViewFor(aEvent->widget); //Find the view to which we're initially going to send the event //for hittesting. if (nsnull != mMouseGrabber && NS_IS_MOUSE_EVENT(aEvent)) { view = mMouseGrabber; } else if (nsnull != mKeyGrabber && NS_IS_KEY_EVENT(aEvent)) { view = mKeyGrabber; } else if (NS_OK == aEvent->widget->QueryInterface(NS_GET_IID(nsIScrollbar), (void**)&sb)) { view = baseView; NS_RELEASE(sb); } else { view = mRootView; } if (nsnull != view) { //Calculate the proper offset for the view we're going to offset.x = offset.y = 0; if (baseView != view) { //Get offset from root of baseView nsIView *parent; nsRect bounds; parent = baseView; while (nsnull != parent) { parent->GetBounds(bounds); offset.x += bounds.x; offset.y += bounds.y; parent->GetParent(parent); } //Subtract back offset from root of view parent = view; while (nsnull != parent) { parent->GetBounds(bounds); offset.x -= bounds.x; offset.y -= bounds.y; parent->GetParent(parent); } } //Dispatch the event float p2t, t2p; mContext->GetDevUnitsToAppUnits(p2t); mContext->GetAppUnitsToDevUnits(t2p); //Before we start mucking with coords, make sure we know our baseline aEvent->refPoint.x = aEvent->point.x; aEvent->refPoint.y = aEvent->point.y; aEvent->point.x = NSIntPixelsToTwips(aEvent->point.x, p2t); aEvent->point.y = NSIntPixelsToTwips(aEvent->point.y, p2t); aEvent->point.x += offset.x; aEvent->point.y += offset.y; PRBool handled = PR_FALSE; view->HandleEvent(aEvent, NS_VIEW_FLAG_CHECK_CHILDREN | NS_VIEW_FLAG_CHECK_PARENT | NS_VIEW_FLAG_CHECK_SIBLINGS, aStatus, handled); aEvent->point.x -= offset.x; aEvent->point.y -= offset.y; aEvent->point.x = NSTwipsToIntPixels(aEvent->point.x, t2p); aEvent->point.y = NSTwipsToIntPixels(aEvent->point.y, t2p); // // if the event is an nsTextEvent, we need to map the reply back into platform coordinates // if (aEvent->message==NS_TEXT_EVENT) { ((nsTextEvent*)aEvent)->theReply.mCursorPosition.x=NSTwipsToIntPixels(((nsTextEvent*)aEvent)->theReply.mCursorPosition.x, t2p); ((nsTextEvent*)aEvent)->theReply.mCursorPosition.y=NSTwipsToIntPixels(((nsTextEvent*)aEvent)->theReply.mCursorPosition.y, t2p); } if (aEvent->message==NS_COMPOSITION_START) { ((nsCompositionEvent*)aEvent)->theReply.mCursorPosition.x=NSTwipsToIntPixels(((nsCompositionEvent*)aEvent)->theReply.mCursorPosition.x,t2p); ((nsCompositionEvent*)aEvent)->theReply.mCursorPosition.y=NSTwipsToIntPixels(((nsCompositionEvent*)aEvent)->theReply.mCursorPosition.y,t2p); } } break; } } return NS_OK; } NS_IMETHODIMP nsViewManager :: GrabMouseEvents(nsIView *aView, PRBool &aResult) { mMouseGrabber = aView; aResult = PR_TRUE; return NS_OK; } NS_IMETHODIMP nsViewManager :: GrabKeyEvents(nsIView *aView, PRBool &aResult) { mKeyGrabber = aView; aResult = PR_TRUE; return NS_OK; } NS_IMETHODIMP nsViewManager :: GetMouseEventGrabber(nsIView *&aView) { aView = mMouseGrabber; return NS_OK; } NS_IMETHODIMP nsViewManager :: GetKeyEventGrabber(nsIView *&aView) { aView = mKeyGrabber; return NS_OK; } NS_IMETHODIMP nsViewManager :: InsertChild(nsIView *parent, nsIView *child, nsIView *sibling, PRBool above) { NS_PRECONDITION(nsnull != parent, "null ptr"); NS_PRECONDITION(nsnull != child, "null ptr"); if ((nsnull != parent) && (nsnull != child)) { nsIView *kid; nsIView *prev = nsnull; //verify that the sibling exists... parent->GetChild(0, kid); while (nsnull != kid) { if (kid == sibling) break; //get the next sibling view prev = kid; kid->GetNextSibling(kid); } if (nsnull != kid) { //it's there, so do the insertion if (PR_TRUE == above) parent->InsertChild(child, prev); else parent->InsertChild(child, sibling); } UpdateTransCnt(nsnull, child); //and mark this area as dirty if the view is visible... nsViewVisibility visibility; child->GetVisibility(visibility); if (nsViewVisibility_kHide != visibility) UpdateView(child, NS_VMREFRESH_NO_SYNC); } return NS_OK; } NS_IMETHODIMP nsViewManager :: InsertChild(nsIView *parent, nsIView *child, PRInt32 zindex) { NS_PRECONDITION(nsnull != parent, "null ptr"); NS_PRECONDITION(nsnull != child, "null ptr"); if ((nsnull != parent) && (nsnull != child)) { nsIView *kid; nsIView *prev = nsnull; //find the right insertion point... parent->GetChild(0, kid); while (nsnull != kid) { PRInt32 idx; kid->GetZIndex(idx); if (zindex >= idx) break; //get the next sibling view prev = kid; kid->GetNextSibling(kid); } //in case this hasn't been set yet... maybe we should not do this? MMP child->SetZIndex(zindex); parent->InsertChild(child, prev); UpdateTransCnt(nsnull, child); //and mark this area as dirty if the view is visible... nsViewVisibility visibility; child->GetVisibility(visibility); if (nsViewVisibility_kHide != visibility) UpdateView(child, NS_VMREFRESH_NO_SYNC); } return NS_OK; } NS_IMETHODIMP nsViewManager :: RemoveChild(nsIView *parent, nsIView *child) { NS_PRECONDITION(nsnull != parent, "null ptr"); NS_PRECONDITION(nsnull != child, "null ptr"); if ((nsnull != parent) && (nsnull != child)) { UpdateTransCnt(child, nsnull); UpdateView(child, NS_VMREFRESH_NO_SYNC); parent->RemoveChild(child); } return NS_OK; } NS_IMETHODIMP nsViewManager :: MoveViewBy(nsIView *aView, nscoord aX, nscoord aY) { nscoord x, y; aView->GetPosition(&x, &y); MoveViewTo(aView, aX + x, aY + y); return NS_OK; } NS_IMETHODIMP nsViewManager::MoveViewTo(nsIView *aView, nscoord aX, nscoord aY) { nscoord oldX, oldY; aView->GetPosition(&oldX, &oldY); aView->SetPosition(aX, aY); // only do damage control if the view is visible if ((aX != oldX) || (aY != oldY)) { nsViewVisibility visibility; aView->GetVisibility(visibility); if (visibility != nsViewVisibility_kHide) { nsRect bounds; aView->GetBounds(bounds); nsRect oldArea(oldX, oldY, bounds.width, bounds.height); nsIView* parentView; aView->GetParent(parentView); UpdateView(parentView, oldArea, NS_VMREFRESH_NO_SYNC); nsRect newArea(aX, aY, bounds.width, bounds.height); UpdateView(parentView, newArea, NS_VMREFRESH_NO_SYNC); } } return NS_OK; } NS_IMETHODIMP nsViewManager::ResizeView(nsIView *aView, nscoord width, nscoord height) { nscoord oldWidth, oldHeight; aView->GetDimensions(&oldWidth, &oldHeight); if ((width != oldWidth) || (height != oldHeight)) { nscoord x = 0, y = 0; nsIView* parentView = nsnull; aView->GetParent(parentView); if (parentView != nsnull) aView->GetPosition(&x, &y); else parentView = aView; // resize the view. aView->SetDimensions(width, height); #if 0 // refresh the bounding box of old and new areas. nscoord maxWidth = (oldWidth < width ? width : oldWidth); nscoord maxHeight = (oldHeight < height ? height : oldHeight); nsRect boundingArea(x, y, maxWidth, maxHeight); UpdateView(parentView, boundingArea, NS_VMREFRESH_NO_SYNC); #else // brute force, invalidate old and new areas. I don't understand // why just refreshing the bounding box is insufficient. nsRect oldBounds(x, y, oldWidth, oldHeight); UpdateView(parentView, oldBounds, NS_VMREFRESH_NO_SYNC); UpdateView(parentView, NS_VMREFRESH_NO_SYNC); #endif } return NS_OK; } NS_IMETHODIMP nsViewManager :: SetViewClip(nsIView *aView, nsRect *aRect) { NS_ASSERTION(!(nsnull == aView), "no view"); NS_ASSERTION(!(nsnull == aRect), "no clip"); aView->SetClip(aRect->x, aRect->y, aRect->XMost(), aRect->YMost()); UpdateView(aView, *aRect, NS_VMREFRESH_NO_SYNC); return NS_OK; } NS_IMETHODIMP nsViewManager :: SetViewVisibility(nsIView *aView, nsViewVisibility aVisible) { nsViewVisibility oldVisible; aView->GetVisibility(oldVisible); if (aVisible != oldVisible) { aView->SetVisibility(aVisible); if (nsViewVisibility_kHide == aVisible) { nsIView* parentView = nsnull; aView->GetParent(parentView); if (parentView) { nsRect bounds; aView->GetBounds(bounds); UpdateView(parentView, bounds, NS_VMREFRESH_NO_SYNC); } } else { UpdateView(aView, NS_VMREFRESH_NO_SYNC); } } return NS_OK; } NS_IMETHODIMP nsViewManager :: SetViewZIndex(nsIView *aView, PRInt32 aZIndex) { nsresult rv; NS_ASSERTION(!(nsnull == aView), "no view"); PRInt32 oldidx; aView->GetZIndex(oldidx); if (oldidx != aZIndex) { nsIView *parent; aView->GetParent(parent); if (nsnull != parent) { //we don't just call the view manager's RemoveChild() //so that we can avoid two trips trough the UpdateView() //code (one for removal, one for insertion). MMP parent->RemoveChild(aView); UpdateTransCnt(aView, nsnull); rv = InsertChild(parent, aView, aZIndex); } else rv = NS_OK; } else rv = NS_OK; return rv; } NS_IMETHODIMP nsViewManager::SetViewAutoZIndex(nsIView *aView, PRBool aAutoZIndex) { return aView->SetAutoZIndex(aAutoZIndex); } NS_IMETHODIMP nsViewManager :: MoveViewAbove(nsIView *aView, nsIView *aOther) { nsresult rv; NS_ASSERTION(!(nsnull == aView), "no view"); NS_ASSERTION(!(nsnull == aOther), "no view"); nsIView *nextview; aView->GetNextSibling(nextview); if (nextview != aOther) { nsIView *parent; aView->GetParent(parent); if (nsnull != parent) { //we don't just call the view manager's RemoveChild() //so that we can avoid two trips trough the UpdateView() //code (one for removal, one for insertion). MMP parent->RemoveChild(aView); UpdateTransCnt(aView, nsnull); rv = InsertChild(parent, aView, aOther, PR_TRUE); } else rv = NS_OK; } else rv = NS_OK; return rv; } NS_IMETHODIMP nsViewManager :: MoveViewBelow(nsIView *aView, nsIView *aOther) { nsresult rv; NS_ASSERTION(!(nsnull == aView), "no view"); NS_ASSERTION(!(nsnull == aOther), "no view"); nsIView *nextview; aOther->GetNextSibling(nextview); if (nextview != aView) { nsIView *parent; aView->GetParent(parent); if (nsnull != parent) { //we don't just call the view manager's RemoveChild() //so that we can avoid two trips trough the UpdateView() //code (one for removal, one for insertion). MMP parent->RemoveChild(aView); UpdateTransCnt(aView, nsnull); rv = InsertChild(parent, aView, aOther, PR_FALSE); } else rv = NS_OK; } else rv = NS_OK; return rv; } NS_IMETHODIMP nsViewManager :: IsViewShown(nsIView *aView, PRBool &aResult) { aResult = PR_TRUE; return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsViewManager :: GetViewClipAbsolute(nsIView *aView, nsRect *rect, PRBool &aResult) { aResult = PR_TRUE; return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsViewManager :: SetViewContentTransparency(nsIView *aView, PRBool aTransparent) { PRBool trans; aView->HasTransparency(trans); if (trans != aTransparent) { UpdateTransCnt(aView, nsnull); aView->SetContentTransparency(aTransparent); UpdateTransCnt(nsnull, aView); UpdateView(aView, NS_VMREFRESH_NO_SYNC); } return NS_OK; } NS_IMETHODIMP nsViewManager :: SetViewOpacity(nsIView *aView, float aOpacity) { float opacity; aView->GetOpacity(opacity); if (opacity != aOpacity) { UpdateTransCnt(aView, nsnull); aView->SetOpacity(aOpacity); UpdateTransCnt(nsnull, aView); UpdateView(aView, NS_VMREFRESH_NO_SYNC); } return NS_OK; } NS_IMETHODIMP nsViewManager :: SetViewObserver(nsIViewObserver *aObserver) { mObserver = aObserver; return NS_OK; } NS_IMETHODIMP nsViewManager :: GetViewObserver(nsIViewObserver *&aObserver) { if (nsnull != mObserver) { aObserver = mObserver; NS_ADDREF(mObserver); return NS_OK; } else return NS_ERROR_NO_INTERFACE; } NS_IMETHODIMP nsViewManager :: GetDeviceContext(nsIDeviceContext *&aContext) { NS_IF_ADDREF(mContext); aContext = mContext; return NS_OK; } #ifndef max #define max(a, b) ((a) < (b) ? (b) : (a)) #endif nsDrawingSurface nsViewManager :: GetDrawingSurface(nsIRenderingContext &aContext, nsRect& aBounds) { if ((nsnull == mDrawingSurface) || (mDSBounds.width < aBounds.width) || (mDSBounds.height < aBounds.height)) { nsRect newBounds; newBounds.MoveTo(aBounds.x, aBounds.y); newBounds.width = max(aBounds.width, mDSBounds.width); newBounds.height = max(aBounds.height, mDSBounds.height); if (mDrawingSurface) { //destroy existing DS aContext.DestroyDrawingSurface(mDrawingSurface); mDrawingSurface = nsnull; } nsresult rv = aContext.CreateDrawingSurface(&newBounds, 0, mDrawingSurface); if (NS_SUCCEEDED(rv)) { mDSBounds = newBounds; aContext.SelectOffScreenDrawingSurface(mDrawingSurface); } else { mDSBounds.SetRect(0,0,0,0); mDrawingSurface = nsnull; } } else { aContext.SelectOffScreenDrawingSurface(mDrawingSurface); float p2t; mContext->GetDevUnitsToAppUnits(p2t); nsRect bounds = aBounds; bounds *= p2t; PRBool clipEmpty; aContext.SetClipRect(bounds, nsClipCombine_kReplace, clipEmpty); nscolor col = NS_RGB(255,255,255); aContext.SetColor(col); //aContext.FillRect(bounds); } return mDrawingSurface; } NS_IMETHODIMP nsViewManager :: ShowQuality(PRBool aShow) { if (nsnull != mRootScrollable) mRootScrollable->ShowQuality(aShow); return NS_OK; } NS_IMETHODIMP nsViewManager :: GetShowQuality(PRBool &aResult) { if (nsnull != mRootScrollable) mRootScrollable->GetShowQuality(aResult); return NS_OK; } NS_IMETHODIMP nsViewManager :: SetQuality(nsContentQuality aQuality) { if (nsnull != mRootScrollable) mRootScrollable->SetQuality(aQuality); return NS_OK; } nsIRenderingContext * nsViewManager :: CreateRenderingContext(nsIView &aView) { nsIView *par = &aView; nsIWidget *win; nsIRenderingContext *cx = nsnull; nscoord x, y, ax = 0, ay = 0; do { par->GetWidget(win); if (nsnull != win) break; //get absolute coordinates of view, but don't //add in view pos since the first thing you ever //need to do when painting a view is to translate //the rendering context by the views pos and other parts //of the code do this for us... if (par != &aView) { par->GetPosition(&x, &y); ax += x; ay += y; } par->GetParent(par); } while (nsnull != par); if (nsnull != win) { mContext->CreateRenderingContext(&aView, cx); if (nsnull != cx) cx->Translate(ax, ay); NS_RELEASE(win); } return cx; } void nsViewManager::AddRectToDirtyRegion(nsIView* aView, const nsRect &aRect) const { // Find a view with an associated widget. We'll transform this rect from the // current view's coordinate system to a "heavyweight" parent view, then convert // the rect to pixel coordinates, and accumulate the rect into that view's dirty region. nsRect widgetRect = aRect; nsIView* widgetView = GetWidgetView(aView); ViewToWidget(aView, widgetView, widgetRect); // Get the dirty region associated with the widget view nsCOMPtr dirtyRegion; if (NS_SUCCEEDED(widgetView->GetDirtyRegion(*getter_AddRefs(dirtyRegion)))) { // add this rect to the widget view's dirty region. dirtyRegion->Union(widgetRect.x, widgetRect.y, widgetRect.width, widgetRect.height); } } void nsViewManager::UpdateTransCnt(nsIView *oldview, nsIView *newview) { if (nsnull != oldview) { PRBool hasTransparency; float opacity; oldview->HasTransparency(hasTransparency); oldview->GetOpacity(opacity); if (hasTransparency || (1.0f != opacity)) mTransCnt--; } if (nsnull != newview) { PRBool hasTransparency; float opacity; newview->HasTransparency(hasTransparency); newview->GetOpacity(opacity); if (hasTransparency || (1.0f != opacity)) mTransCnt++; } } NS_IMETHODIMP nsViewManager :: DisableRefresh(void) { if (mUpdateBatchCnt > 0) return NS_OK; mRefreshEnabled = PR_FALSE; return NS_OK; } NS_IMETHODIMP nsViewManager :: EnableRefresh(void) { if (mUpdateBatchCnt > 0) return NS_OK; mRefreshEnabled = PR_TRUE; if (mUpdateCnt > 0) ProcessPendingUpdates(mRootView); if (mTrueFrameRate > 0) { PRInt32 deltams = PR_IntervalToMilliseconds(PR_IntervalNow() - mLastRefresh); if (deltams > (1000 / (PRInt32)mTrueFrameRate)) Composite(); } return NS_OK; } NS_IMETHODIMP nsViewManager :: BeginUpdateViewBatch(void) { nsresult result = NS_OK; if (mUpdateBatchCnt == 0) result = DisableRefresh(); if (NS_SUCCEEDED(result)) ++mUpdateBatchCnt; return result; } NS_IMETHODIMP nsViewManager :: EndUpdateViewBatch(void) { nsresult result = NS_OK; --mUpdateBatchCnt; NS_ASSERTION(mUpdateBatchCnt >= 0, "Invalid batch count!"); if (mUpdateBatchCnt < 0) { mUpdateBatchCnt = 0; return NS_ERROR_FAILURE; } if (mUpdateBatchCnt == 0) result = EnableRefresh(); return result; } NS_IMETHODIMP nsViewManager :: SetRootScrollableView(nsIScrollableView *aScrollable) { mRootScrollable = aScrollable; //XXX this needs to go away when layout start setting this bit on it's own. MMP if (mRootScrollable) mRootScrollable->SetScrollProperties(NS_SCROLL_PROPERTY_ALWAYS_BLIT); return NS_OK; } NS_IMETHODIMP nsViewManager :: GetRootScrollableView(nsIScrollableView **aScrollable) { *aScrollable = mRootScrollable; return NS_OK; } NS_IMETHODIMP nsViewManager :: Display(nsIView* aView) { nsIRenderingContext *localcx = nsnull; nsRect trect; if (PR_FALSE == mRefreshEnabled) return NS_OK; NS_ASSERTION(!(PR_TRUE == mPainting), "recursive painting not permitted"); mPainting = PR_TRUE; mContext->CreateRenderingContext(localcx); //couldn't get rendering context. this is ok if at startup if (nsnull == localcx) { return NS_ERROR_FAILURE; } aView->GetBounds(trect); nscoord x = trect.x, y = trect.y; // XXX Temporarily reset the position to (0, 0), that way when we paint // we won't end up translating incorrectly aView->SetPosition(0, 0); PRBool result; trect.x = trect.y = 0; localcx->SetClipRect(trect, nsClipCombine_kReplace, result); // Paint the view. The clipping rect was set above set don't clip again. aView->Paint(*localcx, trect, NS_VIEW_FLAG_CLIP_SET, result); // XXX Reset the view's origin aView->SetPosition(x, y); NS_RELEASE(localcx); mPainting = PR_FALSE; return NS_OK; } NS_IMETHODIMP nsViewManager :: AddCompositeListener(nsICompositeListener* aListener) { if (nsnull == mCompositeListeners) { nsresult rv = NS_NewISupportsArray(&mCompositeListeners); if (NS_FAILED(rv)) return rv; } return mCompositeListeners->AppendElement(aListener); } NS_IMETHODIMP nsViewManager :: RemoveCompositeListener(nsICompositeListener* aListener) { if (nsnull != mCompositeListeners) { return mCompositeListeners->RemoveElement(aListener); } return NS_ERROR_FAILURE; } PRBool nsViewManager :: CreateDisplayList(nsIView *aView, PRInt32 *aIndex, nscoord aOriginX, nscoord aOriginY, nsIView *aRealView, const nsRect *aDamageRect, nsIView *aTopView, nsVoidArray *aArray, nscoord aX, nscoord aY) { PRInt32 numkids, zindex; PRBool hasWidget, retval = PR_FALSE; nsIClipView *clipper = nsnull; nsPoint *point; nsIView *child = nsnull; NS_ASSERTION(!(!aView), "no view"); NS_ASSERTION(!(!aIndex), "no index"); if (!aArray) { if (!mDisplayList) mDisplayList = new nsVoidArray(8); aArray = mDisplayList; if (!aArray) return PR_TRUE; } if (!aTopView) aTopView = aView; nsRect lrect; aView->GetBounds(lrect); if (aView == aTopView) { lrect.x = 0; lrect.y = 0; } lrect.x += aX; lrect.y += aY; aView->QueryInterface(NS_GET_IID(nsIClipView), (void **)&clipper); aView->GetChildCount(numkids); aView->GetScratchPoint(&point); hasWidget = DoesViewHaveNativeWidget(*aView); if (numkids > 0) { if (clipper && (!hasWidget || (hasWidget && point->x))) { lrect.x -= aOriginX; lrect.y -= aOriginY; retval = AddToDisplayList(aArray, aIndex, aView, lrect, PUSH_CLIP); if (retval) return retval; lrect.x += aOriginX; lrect.y += aOriginY; } if (!hasWidget || (hasWidget && point->x)) // if ((aView == aTopView) || (aView == aRealView)) // if ((aView == aTopView) || !hasWidget || (aView == aRealView)) // if ((aView == aTopView) || !(hasWidget && clipper) || (aView == aRealView)) { for (aView->GetChild(0, child); nsnull != child; child->GetNextSibling(child)) { child->GetZIndex(zindex); if (zindex < 0) break; retval = CreateDisplayList(child, aIndex, aOriginX, aOriginY, aRealView, aDamageRect, aTopView, aArray, lrect.x, lrect.y); if (retval) break; } } } lrect.x -= aOriginX; lrect.y -= aOriginY; // if (clipper) if (clipper && (!hasWidget || (hasWidget && point->x))) { if (numkids > 0) retval = AddToDisplayList(aArray, aIndex, aView, lrect, POP_CLIP); } else if (!retval) { nsViewVisibility vis; float opacity; PRBool overlap; PRBool trans; nsRect irect; aView->GetVisibility(vis); aView->GetOpacity(opacity); aView->HasTransparency(trans); if (aDamageRect) overlap = irect.IntersectRect(lrect, *aDamageRect); else overlap = PR_TRUE; if ((nsViewVisibility_kShow == vis) && (opacity > 0.0f) && overlap) { retval = AddToDisplayList(aArray, aIndex, aView, lrect, 0); if (retval || !trans && (opacity == 1.0f) && (irect == *aDamageRect)) retval = PR_TRUE; } // any children with negative z-indices? if (!retval && nsnull != child) { lrect.x += aOriginX; lrect.y += aOriginY; for (; nsnull != child; child->GetNextSibling(child)) { retval = CreateDisplayList(child, aIndex, aOriginX, aOriginY, aRealView, aDamageRect, aTopView, aArray, lrect.x, lrect.y); if (retval) break; } } } return retval; } PRBool nsViewManager :: AddToDisplayList(nsVoidArray *aArray, PRInt32 *aIndex, nsIView *aView, nsRect &aRect, PRUint32 aFlags) { PRInt32 index = (*aIndex)++; DisplayListElement* element = (DisplayListElement*) mDisplayList->ElementAt(index); if (element == nsnull) { element = new DisplayListElement; if (element == nsnull) { *aIndex = index; return PR_TRUE; } mDisplayList->ReplaceElementAt(element, index); } element->mView = aView; element->mClip = aRect; element->mFlags = aFlags; return PR_FALSE; } void nsViewManager::ShowDisplayList(PRInt32 flatlen) { char nest[400]; PRInt32 newnestcnt, nestcnt = 0, cnt; for (cnt = 0; cnt < 400; cnt++) nest[cnt] = ' '; float t2p; mContext->GetAppUnitsToDevUnits(t2p); printf("### display list length=%d ###\n", flatlen); for (cnt = 0; cnt < flatlen; cnt += DISPLAYLIST_INC) { nsIView *view, *parent; nsRect rect; PRUint32 flags; PRInt32 zindex; DisplayListElement* element = (DisplayListElement*) mDisplayList->ElementAt(cnt); view = element->mView; rect = element->mClip; flags = element->mFlags; nest[nestcnt << 1] = 0; view->GetParent(parent); view->GetZIndex(zindex); rect *= t2p; printf("%snsIView@%p [z=%d, x=%d, y=%d, w=%d, h=%d, p=%p]\n", nest, view, zindex, rect.x, rect.y, rect.width, rect.height, parent); newnestcnt = nestcnt; if (flags) { printf("%s", nest); if (flags & POP_CLIP) { printf("POP_CLIP "); newnestcnt--; } if (flags & PUSH_CLIP) { printf("PUSH_CLIP "); newnestcnt++; } if (flags & VIEW_INCLUDED) printf("VIEW_INCLUDED "); printf("\n"); } nest[nestcnt << 1] = ' '; nestcnt = newnestcnt; } } void nsViewManager :: ComputeViewOffset(nsIView *aView, nscoord *aX, nscoord *aY, PRInt32 aFlag) { nsIView *parent; nsRect bounds; nsPoint *point; aView->GetScratchPoint(&point); point->x = aFlag; aView->GetBounds(bounds); if (aX && aY) { *aX += bounds.x; *aY += bounds.y; } aView->GetParent(parent); if (parent) ComputeViewOffset(parent, aX, aY, aFlag); } PRBool nsViewManager :: DoesViewHaveNativeWidget(nsIView &aView) { nsIWidget *widget; PRBool retval = PR_FALSE; aView.GetWidget(widget); if (nsnull != widget) { void *nativewidget; nativewidget = widget->GetNativeData(NS_NATIVE_WIDGET); NS_RELEASE(widget); if (nsnull != nativewidget) retval = PR_TRUE; } return retval; } void nsViewManager :: PauseTimer(void) { PRUint32 oldframerate = mTrueFrameRate; SetFrameRate(0); mTrueFrameRate = oldframerate; } void nsViewManager :: RestartTimer(void) { SetFrameRate(mTrueFrameRate); } nsIView* nsViewManager::GetWidgetView(nsIView *aView) const { while (aView != nsnull) { PRBool hasWidget; aView->HasWidget(&hasWidget); if (hasWidget) return aView; aView->GetParent(aView); } return nsnull; } void nsViewManager::ViewToWidget(nsIView *aView, nsIView* aWidgetView, nsRect &aRect) const { while (aView != aWidgetView) { nscoord x, y; aView->GetPosition(&x, &y); aRect.MoveBy(x, y); aView->GetParent(aView); } // intersect aRect with bounds of aWidgetView, to prevent generating any illegal rectangles. nsRect bounds; aWidgetView->GetBounds(bounds); bounds.x = bounds.y = 0; aRect.IntersectRect(aRect, bounds); // finally, convert to device coordinates. float t2p; mContext->GetAppUnitsToDevUnits(t2p); aRect.ScaleRoundOut(t2p); }