Bug 441233 Missing focus events in Thunderbird account wizard dialog r=surkov.alexander
[wine-gecko.git] / view / src / nsViewManager.cpp
blobdeaa5c19a1ee90bf1c08db31acfd5ea3e196a0d6
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Patrick C. Beard <beard@netscape.com>
24 * Kevin McCluskey <kmcclusk@netscape.com>
25 * Robert O'Callahan <roc+@cs.cmu.edu>
26 * Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
28 * Alternatively, the contents of this file may be used under the terms of
29 * either of the GNU General Public License Version 2 or later (the "GPL"),
30 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the MPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the MPL, the GPL or the LGPL.
40 * ***** END LICENSE BLOCK ***** */
42 #define PL_ARENA_CONST_ALIGN_MASK (sizeof(void*)-1)
43 #include "plarena.h"
45 #include "nsAutoPtr.h"
46 #include "nsViewManager.h"
47 #include "nsIRenderingContext.h"
48 #include "nsIDeviceContext.h"
49 #include "nsGfxCIID.h"
50 #include "nsIScrollableView.h"
51 #include "nsView.h"
52 #include "nsISupportsArray.h"
53 #include "nsCOMPtr.h"
54 #include "nsIServiceManager.h"
55 #include "nsGUIEvent.h"
56 #include "nsIPrefBranch.h"
57 #include "nsIPrefService.h"
58 #include "nsRegion.h"
59 #include "nsInt64.h"
60 #include "nsScrollPortView.h"
61 #include "nsHashtable.h"
62 #include "nsCOMArray.h"
63 #include "nsThreadUtils.h"
64 #include "nsContentUtils.h"
65 #include "gfxContext.h"
66 #define NS_STATIC_FOCUS_SUPPRESSOR
67 #include "nsIFocusEventSuppressor.h"
69 static NS_DEFINE_IID(kBlenderCID, NS_BLENDER_CID);
70 static NS_DEFINE_IID(kRegionCID, NS_REGION_CID);
71 static NS_DEFINE_IID(kRenderingContextCID, NS_RENDERING_CONTEXT_CID);
73 /**
74 XXX TODO XXX
76 DeCOMify newly private methods
77 Optimize view storage
80 /**
81 A note about platform assumptions:
83 We assume all native widgets are opaque.
85 We assume that a widget is z-ordered on top of its parent.
87 We do NOT assume anything about the relative z-ordering of sibling widgets. Even though
88 we ask for a specific z-order, we don't assume that widget z-ordering actually works.
91 #define NSCOORD_NONE PR_INT32_MIN
93 #ifdef NS_VM_PERF_METRICS
94 #include "nsITimeRecorder.h"
95 #endif
97 #ifdef DEBUG_smaug
98 #define DEBUG_FOCUS_SUPPRESSION
99 #endif
101 //-------------- Begin Invalidate Event Definition ------------------------
103 class nsInvalidateEvent : public nsViewManagerEvent {
104 public:
105 nsInvalidateEvent(nsViewManager *vm) : nsViewManagerEvent(vm) {}
107 NS_IMETHOD Run() {
108 if (mViewManager)
109 mViewManager->ProcessInvalidateEvent();
110 return NS_OK;
114 //-------------- End Invalidate Event Definition ---------------------------
116 static PRBool IsViewVisible(nsView *aView)
118 for (nsIView *view = aView; view; view = view->GetParent()) {
119 // We don't check widget visibility here because in the future (with
120 // the better approach to this that's in attachment 160801 on bug
121 // 227361), callers of the equivalent to this function should be able
122 // to rely on being notified when the result of this function changes.
123 if (view->GetVisibility() == nsViewVisibility_kHide)
124 return PR_FALSE;
126 // Find out if the root view is visible by asking the view observer
127 // (this won't be needed anymore if we link view trees across chrome /
128 // content boundaries in DocumentViewerImpl::MakeWindow).
129 nsIViewObserver* vo = aView->GetViewManager()->GetViewObserver();
130 return vo && vo->IsVisible();
133 void
134 nsViewManager::PostInvalidateEvent()
136 NS_ASSERTION(IsRootVM(), "Caller screwed up");
138 if (!mInvalidateEvent.IsPending()) {
139 nsRefPtr<nsViewManagerEvent> ev = new nsInvalidateEvent(this);
140 if (NS_FAILED(NS_DispatchToCurrentThread(ev))) {
141 NS_WARNING("failed to dispatch nsInvalidateEvent");
142 } else {
143 mInvalidateEvent = ev;
148 #undef DEBUG_MOUSE_LOCATION
150 PRInt32 nsViewManager::mVMCount = 0;
151 nsIRenderingContext* nsViewManager::gCleanupContext = nsnull;
153 // Weakly held references to all of the view managers
154 nsVoidArray* nsViewManager::gViewManagers = nsnull;
155 PRUint32 nsViewManager::gLastUserEventTime = 0;
157 nsViewManager::nsViewManager()
158 : mMouseLocation(NSCOORD_NONE, NSCOORD_NONE)
159 , mDelayedResize(NSCOORD_NONE, NSCOORD_NONE)
160 , mRootViewManager(this)
162 if (gViewManagers == nsnull) {
163 NS_ASSERTION(mVMCount == 0, "View Manager count is incorrect");
164 // Create an array to hold a list of view managers
165 gViewManagers = new nsVoidArray;
168 if (gCleanupContext == nsnull) {
169 /* XXX: This should use a device to create a matching |nsIRenderingContext| object */
170 CallCreateInstance(kRenderingContextCID, &gCleanupContext);
171 NS_ASSERTION(gCleanupContext,
172 "Wasn't able to create a graphics context for cleanup");
175 gViewManagers->AppendElement(this);
177 if (++mVMCount == 1) {
178 NS_AddFocusSuppressorCallback(&nsViewManager::SuppressFocusEvents);
180 // NOTE: we use a zeroing operator new, so all data members are
181 // assumed to be cleared here.
182 mDefaultBackgroundColor = NS_RGBA(0, 0, 0, 0);
183 mHasPendingUpdates = PR_FALSE;
184 mRecursiveRefreshPending = PR_FALSE;
185 mUpdateBatchFlags = 0;
188 nsViewManager::~nsViewManager()
190 if (mRootView) {
191 // Destroy any remaining views
192 mRootView->Destroy();
193 mRootView = nsnull;
196 // Make sure to revoke pending events for all viewmanagers, since some events
197 // are posted by a non-root viewmanager.
198 mInvalidateEvent.Revoke();
199 mSynthMouseMoveEvent.Revoke();
201 if (!IsRootVM()) {
202 // We have a strong ref to mRootViewManager
203 NS_RELEASE(mRootViewManager);
206 mRootScrollable = nsnull;
208 NS_ASSERTION((mVMCount > 0), "underflow of viewmanagers");
209 --mVMCount;
211 #ifdef DEBUG
212 PRBool removed =
213 #endif
214 gViewManagers->RemoveElement(this);
215 NS_ASSERTION(removed, "Viewmanager instance not was not in the global list of viewmanagers");
217 if (0 == mVMCount) {
218 // There aren't any more view managers so
219 // release the global array of view managers
221 NS_ASSERTION(gViewManagers != nsnull, "About to delete null gViewManagers");
222 delete gViewManagers;
223 gViewManagers = nsnull;
225 // Cleanup all of the offscreen drawing surfaces if the last view manager
226 // has been destroyed and there is something to cleanup
228 // Note: A global rendering context is needed because it is not possible
229 // to create a nsIRenderingContext during the shutdown of XPCOM. The last
230 // viewmanager is typically destroyed during XPCOM shutdown.
231 NS_IF_RELEASE(gCleanupContext);
234 mObserver = nsnull;
235 mContext = nsnull;
238 NS_IMPL_ISUPPORTS1(nsViewManager, nsIViewManager)
240 nsresult
241 nsViewManager::CreateRegion(nsIRegion* *result)
243 nsresult rv;
245 if (!mRegionFactory) {
246 mRegionFactory = do_GetClassObject(kRegionCID, &rv);
247 if (NS_FAILED(rv)) {
248 *result = nsnull;
249 return rv;
253 nsIRegion* region = nsnull;
254 rv = CallCreateInstance(mRegionFactory.get(), &region);
255 if (NS_SUCCEEDED(rv)) {
256 rv = region->Init();
257 *result = region;
259 return rv;
262 // We don't hold a reference to the presentation context because it
263 // holds a reference to us.
264 NS_IMETHODIMP nsViewManager::Init(nsIDeviceContext* aContext)
266 NS_PRECONDITION(nsnull != aContext, "null ptr");
268 if (nsnull == aContext) {
269 return NS_ERROR_NULL_POINTER;
271 if (nsnull != mContext) {
272 return NS_ERROR_ALREADY_INITIALIZED;
274 mContext = aContext;
276 mRefreshEnabled = PR_TRUE;
278 mMouseGrabber = nsnull;
280 return NS_OK;
283 NS_IMETHODIMP_(nsIView *)
284 nsViewManager::CreateView(const nsRect& aBounds,
285 const nsIView* aParent,
286 nsViewVisibility aVisibilityFlag)
288 nsView *v = new nsView(this, aVisibilityFlag);
289 if (v) {
290 v->SetPosition(aBounds.x, aBounds.y);
291 nsRect dim(0, 0, aBounds.width, aBounds.height);
292 v->SetDimensions(dim, PR_FALSE);
293 v->SetParent(static_cast<nsView*>(const_cast<nsIView*>(aParent)));
295 return v;
298 NS_IMETHODIMP_(nsIScrollableView *)
299 nsViewManager::CreateScrollableView(const nsRect& aBounds,
300 const nsIView* aParent)
302 nsScrollPortView *v = new nsScrollPortView(this);
303 if (v) {
304 v->SetPosition(aBounds.x, aBounds.y);
305 nsRect dim(0, 0, aBounds.width, aBounds.height);
306 v->SetDimensions(dim, PR_FALSE);
307 v->SetParent(static_cast<nsView*>(const_cast<nsIView*>(aParent)));
309 return v;
312 NS_IMETHODIMP nsViewManager::GetRootView(nsIView *&aView)
314 aView = mRootView;
315 return NS_OK;
318 NS_IMETHODIMP nsViewManager::SetRootView(nsIView *aView)
320 nsView* view = static_cast<nsView*>(aView);
322 NS_PRECONDITION(!view || view->GetViewManager() == this,
323 "Unexpected viewmanager on root view");
325 // Do NOT destroy the current root view. It's the caller's responsibility
326 // to destroy it
327 mRootView = view;
329 if (mRootView) {
330 nsView* parent = mRootView->GetParent();
331 if (parent) {
332 // Calling InsertChild on |parent| will InvalidateHierarchy() on us, so
333 // no need to set mRootViewManager ourselves here.
334 parent->InsertChild(mRootView, nsnull);
335 } else {
336 InvalidateHierarchy();
339 mRootView->SetZIndex(PR_FALSE, 0, PR_FALSE);
341 // Else don't touch mRootViewManager
343 return NS_OK;
346 NS_IMETHODIMP nsViewManager::GetWindowDimensions(nscoord *aWidth, nscoord *aHeight)
348 if (nsnull != mRootView) {
349 if (mDelayedResize == nsSize(NSCOORD_NONE, NSCOORD_NONE)) {
350 nsRect dim;
351 mRootView->GetDimensions(dim);
352 *aWidth = dim.width;
353 *aHeight = dim.height;
354 } else {
355 *aWidth = mDelayedResize.width;
356 *aHeight = mDelayedResize.height;
359 else
361 *aWidth = 0;
362 *aHeight = 0;
364 return NS_OK;
367 NS_IMETHODIMP nsViewManager::SetWindowDimensions(nscoord aWidth, nscoord aHeight)
369 if (mRootView) {
370 if (IsViewVisible(mRootView)) {
371 mDelayedResize.SizeTo(NSCOORD_NONE, NSCOORD_NONE);
372 DoSetWindowDimensions(aWidth, aHeight);
373 } else {
374 mDelayedResize.SizeTo(aWidth, aHeight);
378 return NS_OK;
381 static void ConvertNativeRegionToAppRegion(nsIRegion* aIn, nsRegion* aOut,
382 nsIDeviceContext* context)
384 nsRegionRectSet* rects = nsnull;
385 aIn->GetRects(&rects);
386 if (!rects)
387 return;
389 PRInt32 p2a = context->AppUnitsPerDevPixel();
391 PRUint32 i;
392 for (i = 0; i < rects->mNumRects; i++) {
393 const nsRegionRect& inR = rects->mRects[i];
394 nsRect outR;
395 outR.x = NSIntPixelsToAppUnits(inR.x, p2a);
396 outR.y = NSIntPixelsToAppUnits(inR.y, p2a);
397 outR.width = NSIntPixelsToAppUnits(inR.width, p2a);
398 outR.height = NSIntPixelsToAppUnits(inR.height, p2a);
399 aOut->Or(*aOut, outR);
402 aIn->FreeRects(rects);
405 static nsView* GetDisplayRootFor(nsView* aView)
407 nsView *displayRoot = aView;
408 for (;;) {
409 nsView *displayParent = displayRoot->GetParent();
410 if (!displayParent)
411 return displayRoot;
413 if (displayRoot->GetFloating() && !displayParent->GetFloating())
414 return displayRoot;
415 displayRoot = displayParent;
420 aRegion is given in device coordinates!!
422 void nsViewManager::Refresh(nsView *aView, nsIRenderingContext *aContext,
423 nsIRegion *aRegion, PRUint32 aUpdateFlags)
425 NS_ASSERTION(aRegion != nsnull, "Null aRegion");
427 if (! IsRefreshEnabled())
428 return;
430 nsRect viewRect;
431 aView->GetDimensions(viewRect);
433 // damageRegion is the damaged area, in twips, relative to the view origin
434 nsRegion damageRegion;
435 // convert pixels-relative-to-widget-origin to twips-relative-to-widget-origin
436 ConvertNativeRegionToAppRegion(aRegion, &damageRegion, mContext);
437 // move it from widget coordinates into view coordinates
438 damageRegion.MoveBy(viewRect.x, viewRect.y);
440 if (damageRegion.IsEmpty()) {
441 #ifdef DEBUG_roc
442 nsRect damageRect = damageRegion.GetBounds();
443 printf("XXX Damage rectangle (%d,%d,%d,%d) does not intersect the widget's view (%d,%d,%d,%d)!\n",
444 damageRect.x, damageRect.y, damageRect.width, damageRect.height,
445 viewRect.x, viewRect.y, viewRect.width, viewRect.height);
446 #endif
447 return;
450 #ifdef NS_VM_PERF_METRICS
451 MOZ_TIMER_DEBUGLOG(("Reset nsViewManager::Refresh(region), this=%p\n", this));
452 MOZ_TIMER_RESET(mWatch);
454 MOZ_TIMER_DEBUGLOG(("Start: nsViewManager::Refresh(region)\n"));
455 MOZ_TIMER_START(mWatch);
456 #endif
458 NS_ASSERTION(!IsPainting(), "recursive painting not permitted");
459 if (IsPainting()) {
460 RootViewManager()->mRecursiveRefreshPending = PR_TRUE;
461 return;
465 nsAutoScriptBlocker scriptBlocker;
466 SetPainting(PR_TRUE);
468 nsCOMPtr<nsIRenderingContext> localcx;
469 NS_ASSERTION(aView->GetWidget(),
470 "Must have a widget to calculate coordinates correctly");
471 if (nsnull == aContext)
473 localcx = CreateRenderingContext(*aView);
475 //couldn't get rendering context. this is ok at init time atleast
476 if (nsnull == localcx) {
477 SetPainting(PR_FALSE);
478 return;
480 } else {
481 // plain assignment grabs another reference.
482 localcx = aContext;
485 PRInt32 p2a = mContext->AppUnitsPerDevPixel();
487 nsRefPtr<gfxContext> ctx = localcx->ThebesContext();
489 ctx->Save();
491 nsPoint vtowoffset = aView->ViewToWidgetOffset();
492 ctx->Translate(gfxPoint(gfxFloat(vtowoffset.x) / p2a,
493 gfxFloat(vtowoffset.y) / p2a));
495 ctx->Translate(gfxPoint(-gfxFloat(viewRect.x) / p2a,
496 -gfxFloat(viewRect.y) / p2a));
498 nsRegion opaqueRegion;
499 AddCoveringWidgetsToOpaqueRegion(opaqueRegion, mContext, aView);
500 damageRegion.Sub(damageRegion, opaqueRegion);
502 RenderViews(aView, *localcx, damageRegion);
504 ctx->Restore();
506 SetPainting(PR_FALSE);
509 if (RootViewManager()->mRecursiveRefreshPending) {
510 // Unset this flag first, since if aUpdateFlags includes NS_VMREFRESH_IMMEDIATE
511 // we'll reenter this code from the UpdateAllViews call.
512 RootViewManager()->mRecursiveRefreshPending = PR_FALSE;
513 UpdateAllViews(aUpdateFlags);
516 #ifdef NS_VM_PERF_METRICS
517 MOZ_TIMER_DEBUGLOG(("Stop: nsViewManager::Refresh(region), this=%p\n", this));
518 MOZ_TIMER_STOP(mWatch);
519 MOZ_TIMER_LOG(("vm2 Paint time (this=%p): ", this));
520 MOZ_TIMER_PRINT(mWatch);
521 #endif
525 void nsViewManager::DefaultRefresh(nsView* aView, nsIRenderingContext *aContext, const nsRect* aRect)
527 NS_PRECONDITION(aView, "Must have a view to work with!");
528 nsIWidget* widget = aView->GetNearestWidget(nsnull);
529 if (! widget)
530 return;
532 nsCOMPtr<nsIRenderingContext> context = aContext;
533 if (! aContext)
534 context = CreateRenderingContext(*aView);
536 if (! context)
537 return;
539 nscolor bgcolor = mDefaultBackgroundColor;
541 if (NS_GET_A(mDefaultBackgroundColor) == 0) {
542 NS_WARNING("nsViewManager: Asked to paint a default background, but no default background color is set!");
543 return;
546 context->SetColor(bgcolor);
547 context->FillRect(*aRect);
550 void nsViewManager::AddCoveringWidgetsToOpaqueRegion(nsRegion &aRgn, nsIDeviceContext* aContext,
551 nsView* aRootView) {
552 NS_PRECONDITION(aRootView, "Must have root view");
554 // We accumulate the bounds of widgets obscuring aRootView's widget into opaqueRgn.
555 // In OptimizeDisplayList, display list elements which lie behind obscuring native
556 // widgets are dropped.
557 // This shouldn't really be necessary, since the GFX/Widget toolkit should remove these
558 // covering widgets from the clip region passed into the paint command. But right now
559 // they only give us a paint rect and not a region, so we can't access that information.
560 // It's important to identifying areas that are covered by native widgets to avoid
561 // painting their views many times as we process invalidates from the root widget all the
562 // way down to the nested widgets.
564 // NB: we must NOT add widgets that correspond to floating views!
565 // We may be required to paint behind them
566 aRgn.SetEmpty();
568 nsIWidget* widget = aRootView->GetNearestWidget(nsnull);
569 if (!widget) {
570 return;
573 for (nsIWidget* childWidget = widget->GetFirstChild();
574 childWidget;
575 childWidget = childWidget->GetNextSibling()) {
576 PRBool widgetVisible;
577 childWidget->IsVisible(widgetVisible);
578 if (widgetVisible) {
579 nsView* view = nsView::GetViewFor(childWidget);
580 if (view && view->GetVisibility() == nsViewVisibility_kShow
581 && !view->GetFloating()) {
582 nsRect bounds = view->GetBounds();
583 if (bounds.width > 0 && bounds.height > 0) {
584 nsView* viewParent = view->GetParent();
586 while (viewParent && viewParent != aRootView) {
587 viewParent->ConvertToParentCoords(&bounds.x, &bounds.y);
588 viewParent = viewParent->GetParent();
591 // maybe we couldn't get the view into the coordinate
592 // system of aRootView (maybe it's not a descendant
593 // view of aRootView?); if so, don't use it
594 if (viewParent) {
595 aRgn.Or(aRgn, bounds);
603 void nsViewManager::RenderViews(nsView *aView, nsIRenderingContext& aRC,
604 const nsRegion& aRegion)
606 if (mObserver) {
607 nsView* displayRoot = GetDisplayRootFor(aView);
608 nsPoint offsetToRoot = aView->GetOffsetTo(displayRoot);
609 nsRegion damageRegion(aRegion);
610 damageRegion.MoveBy(offsetToRoot);
612 aRC.PushState();
613 aRC.Translate(-offsetToRoot.x, -offsetToRoot.y);
614 mObserver->Paint(displayRoot, &aRC, damageRegion);
615 aRC.PopState();
619 void nsViewManager::ProcessPendingUpdates(nsView* aView, PRBool aDoInvalidate)
621 NS_ASSERTION(IsRootVM(), "Updates will be missed");
623 // Protect against a null-view.
624 if (!aView) {
625 return;
628 if (aView->HasWidget()) {
629 aView->ResetWidgetBounds(PR_FALSE, PR_FALSE, PR_TRUE);
632 // process pending updates in child view.
633 for (nsView* childView = aView->GetFirstChild(); childView;
634 childView = childView->GetNextSibling()) {
635 ProcessPendingUpdates(childView, aDoInvalidate);
638 if (aDoInvalidate && aView->HasNonEmptyDirtyRegion()) {
639 // Push out updates after we've processed the children; ensures that
640 // damage is applied based on the final widget geometry
641 NS_ASSERTION(mRefreshEnabled, "Cannot process pending updates with refresh disabled");
642 nsRegion* dirtyRegion = aView->GetDirtyRegion();
643 if (dirtyRegion) {
644 UpdateWidgetArea(aView, *dirtyRegion, nsnull);
645 dirtyRegion->SetEmpty();
650 NS_IMETHODIMP nsViewManager::Composite()
652 if (!IsRootVM()) {
653 return RootViewManager()->Composite();
656 if (UpdateCount() > 0)
658 ForceUpdate();
659 ClearUpdateCount();
662 return NS_OK;
665 NS_IMETHODIMP nsViewManager::UpdateView(nsIView *aView, PRUint32 aUpdateFlags)
667 // Mark the entire view as damaged
668 nsView* view = static_cast<nsView*>(aView);
670 nsRect bounds = view->GetBounds();
671 view->ConvertFromParentCoords(&bounds.x, &bounds.y);
672 return UpdateView(view, bounds, aUpdateFlags);
675 // This method accumulates the intersectons of all dirty regions attached to
676 // descendants of aSourceView with the cliprect of aTargetView into the dirty
677 // region of aTargetView, after offseting said intersections by aOffset.
678 static void
679 AccumulateIntersectionsIntoDirtyRegion(nsView* aTargetView,
680 nsView* aSourceView,
681 const nsPoint& aOffset)
683 if (aSourceView->HasNonEmptyDirtyRegion()) {
684 // In most cases, aSourceView is an ancestor of aTargetView, since most
685 // commonly we have dirty rects on the root view.
686 nsPoint offset = aTargetView->GetOffsetTo(aSourceView);
687 nsRegion intersection;
688 intersection = *aSourceView->GetDirtyRegion();
689 if (!intersection.IsEmpty()) {
690 nsRegion* targetRegion = aTargetView->GetDirtyRegion();
691 if (targetRegion) {
692 intersection.MoveBy(-offset + aOffset);
693 targetRegion->Or(*targetRegion, intersection);
694 // Random simplification number...
695 targetRegion->SimplifyOutward(20);
700 if (aSourceView == aTargetView) {
701 // No need to do this with kids of aTargetView
702 return;
705 for (nsView* kid = aSourceView->GetFirstChild();
706 kid;
707 kid = kid->GetNextSibling()) {
708 AccumulateIntersectionsIntoDirtyRegion(aTargetView, kid, aOffset);
712 nsresult
713 nsViewManager::WillBitBlit(nsView* aView, nsPoint aScrollAmount)
715 if (!IsRootVM()) {
716 RootViewManager()->WillBitBlit(aView, aScrollAmount);
717 return NS_OK;
720 NS_PRECONDITION(aView, "Must have a view");
721 NS_PRECONDITION(aView->HasWidget(), "View must have a widget");
723 ++mScrollCnt;
725 // Since the view is actually moving the widget by -aScrollAmount, that's the
726 // offset we want to use when accumulating dirty rects.
727 AccumulateIntersectionsIntoDirtyRegion(aView, GetRootView(), -aScrollAmount);
728 return NS_OK;
731 // Invalidate all widgets which overlap the view, other than the view's own widgets.
732 void
733 nsViewManager::UpdateViewAfterScroll(nsView *aView, const nsRegion& aUpdateRegion)
735 NS_ASSERTION(RootViewManager()->mScrollCnt > 0,
736 "Someone forgot to call WillBitBlit()");
737 // Look at the view's clipped rect. It may be that part of the view is clipped out
738 // in which case we don't need to worry about invalidating the clipped-out part.
739 nsRect damageRect = aView->GetDimensions();
740 if (damageRect.IsEmpty()) {
741 // Don't forget to undo mScrollCnt!
742 --RootViewManager()->mScrollCnt;
743 return;
746 nsView* displayRoot = GetDisplayRootFor(aView);
747 nsPoint offset = aView->GetOffsetTo(displayRoot);
748 damageRect.MoveBy(offset);
750 UpdateWidgetArea(displayRoot, nsRegion(damageRect), aView);
751 if (!aUpdateRegion.IsEmpty()) {
752 // XXX We should update the region, not the bounds rect, but that requires
753 // a little more work. Fix this when we reshuffle this code.
754 nsRegion update(aUpdateRegion);
755 update.MoveBy(offset);
756 UpdateWidgetArea(displayRoot, update, nsnull);
757 // FlushPendingInvalidates();
760 Composite();
761 --RootViewManager()->mScrollCnt;
765 * @param aDamagedRegion this region, relative to aWidgetView, is invalidated in
766 * every widget child of aWidgetView, plus aWidgetView's own widget
767 * @param aIgnoreWidgetView if non-null, the aIgnoreWidgetView's widget and its
768 * children are not updated.
770 void
771 nsViewManager::UpdateWidgetArea(nsView *aWidgetView, const nsRegion &aDamagedRegion,
772 nsView* aIgnoreWidgetView)
774 if (!IsRefreshEnabled()) {
775 // accumulate this rectangle in the view's dirty region, so we can
776 // process it later.
777 nsRegion* dirtyRegion = aWidgetView->GetDirtyRegion();
778 if (!dirtyRegion) return;
780 dirtyRegion->Or(*dirtyRegion, aDamagedRegion);
781 // Don't let dirtyRegion grow beyond 8 rects
782 dirtyRegion->SimplifyOutward(8);
783 nsViewManager* rootVM = RootViewManager();
784 rootVM->mHasPendingUpdates = PR_TRUE;
785 rootVM->IncrementUpdateCount();
786 return;
787 // this should only happen at the top level, and this result
788 // should not be consumed by top-level callers, so it doesn't
789 // really matter what we return
792 // If the bounds don't overlap at all, there's nothing to do
793 nsRegion intersection;
794 intersection.And(aWidgetView->GetDimensions(), aDamagedRegion);
795 if (intersection.IsEmpty()) {
796 return;
799 // If the widget is hidden, it don't cover nothing
800 if (nsViewVisibility_kHide == aWidgetView->GetVisibility()) {
801 #ifdef DEBUG
802 // Assert if view is hidden but widget is visible
803 nsIWidget* widget = aWidgetView->GetNearestWidget(nsnull);
804 if (widget) {
805 PRBool visible;
806 widget->IsVisible(visible);
807 NS_ASSERTION(!visible, "View is hidden but widget is visible!");
809 #endif
810 return;
813 if (aWidgetView == aIgnoreWidgetView) {
814 // the widget for aIgnoreWidgetView (and its children) should be treated as already updated.
815 return;
818 nsIWidget* widget = aWidgetView->GetNearestWidget(nsnull);
819 if (!widget) {
820 // The root view or a scrolling view might not have a widget
821 // (for example, during printing). We get here when we scroll
822 // during printing to show selected options in a listbox, for example.
823 return;
826 // Update all child widgets with the damage. In the process,
827 // accumulate the union of all the child widget areas, or at least
828 // some subset of that.
829 nsRegion children;
830 for (nsIWidget* childWidget = widget->GetFirstChild();
831 childWidget;
832 childWidget = childWidget->GetNextSibling()) {
833 nsView* view = nsView::GetViewFor(childWidget);
834 NS_ASSERTION(view != aWidgetView, "will recur infinitely");
835 if (view && view->GetVisibility() == nsViewVisibility_kShow) {
836 // Don't mess with views that are in completely different view
837 // manager trees
838 if (view->GetViewManager()->RootViewManager() == RootViewManager()) {
839 // get the damage region into 'view's coordinate system
840 nsRegion damage = intersection;
841 nsPoint offset = view->GetOffsetTo(aWidgetView);
842 damage.MoveBy(-offset);
843 UpdateWidgetArea(view, damage, aIgnoreWidgetView);
844 children.Or(children, view->GetDimensions() + offset);
845 children.SimplifyInward(20);
850 nsRegion leftOver;
851 leftOver.Sub(intersection, children);
853 if (!leftOver.IsEmpty()) {
854 NS_ASSERTION(IsRefreshEnabled(), "Can only get here with refresh enabled, I hope");
856 const nsRect* r;
857 for (nsRegionRectIterator iter(leftOver); (r = iter.Next());) {
858 nsRect bounds = *r;
859 ViewToWidget(aWidgetView, aWidgetView, bounds);
860 widget->Invalidate(bounds, PR_FALSE);
865 NS_IMETHODIMP nsViewManager::UpdateView(nsIView *aView, const nsRect &aRect, PRUint32 aUpdateFlags)
867 NS_PRECONDITION(nsnull != aView, "null view");
869 nsView* view = static_cast<nsView*>(aView);
871 nsRect damagedRect(aRect);
873 // If the rectangle is not visible then abort
874 // without invalidating. This is a performance
875 // enhancement since invalidating a native widget
876 // can be expensive.
877 // This also checks for silly request like damagedRect.width = 0 or damagedRect.height = 0
878 nsRectVisibility rectVisibility;
879 GetRectVisibility(view, damagedRect, 0, &rectVisibility);
880 if (rectVisibility != nsRectVisibility_kVisible) {
881 return NS_OK;
884 // if this is a floating view, it isn't covered by any widgets other than
885 // its children. In that case we walk up to its parent widget and use
886 // that as the root to update from. This also means we update areas that
887 // may be outside the parent view(s), which is necessary for floats.
888 if (view->GetFloating()) {
889 nsView* widgetParent = view;
891 while (!widgetParent->HasWidget()) {
892 widgetParent->ConvertToParentCoords(&damagedRect.x, &damagedRect.y);
893 widgetParent = widgetParent->GetParent();
896 UpdateWidgetArea(widgetParent, nsRegion(damagedRect), nsnull);
897 } else {
898 // Propagate the update to the root widget of the root view manager, since
899 // iframes, for example, can overlap each other and be translucent. So we
900 // have to possibly invalidate our rect in each of the widgets we have
901 // lying about.
902 damagedRect.MoveBy(ComputeViewOffset(view));
904 UpdateWidgetArea(RootViewManager()->GetRootView(), nsRegion(damagedRect), nsnull);
907 RootViewManager()->IncrementUpdateCount();
909 if (!IsRefreshEnabled()) {
910 return NS_OK;
913 // See if we should do an immediate refresh or wait
914 if (aUpdateFlags & NS_VMREFRESH_IMMEDIATE) {
915 Composite();
918 return NS_OK;
921 NS_IMETHODIMP nsViewManager::UpdateAllViews(PRUint32 aUpdateFlags)
923 if (RootViewManager() != this) {
924 return RootViewManager()->UpdateAllViews(aUpdateFlags);
927 UpdateViews(mRootView, aUpdateFlags);
928 return NS_OK;
931 void nsViewManager::UpdateViews(nsView *aView, PRUint32 aUpdateFlags)
933 // update this view.
934 UpdateView(aView, aUpdateFlags);
936 // update all children as well.
937 nsView* childView = aView->GetFirstChild();
938 while (nsnull != childView) {
939 UpdateViews(childView, aUpdateFlags);
940 childView = childView->GetNextSibling();
944 nsView *nsViewManager::sCurrentlyFocusView = nsnull;
945 nsView *nsViewManager::sViewFocusedBeforeSuppression = nsnull;
946 PRBool nsViewManager::sFocusSuppressed = PR_FALSE;
948 void nsViewManager::SuppressFocusEvents(PRBool aSuppress)
950 if (aSuppress) {
951 sFocusSuppressed = PR_TRUE;
952 SetViewFocusedBeforeSuppression(GetCurrentlyFocusedView());
953 return;
956 sFocusSuppressed = PR_FALSE;
957 if (GetCurrentlyFocusedView() == GetViewFocusedBeforeSuppression()) {
958 return;
961 // We're turning off suppression, synthesize LOSTFOCUS/GOTFOCUS.
962 nsIWidget *widget = nsnull;
963 nsEventStatus status;
965 // Backup what is focused before we send the blur. If the
966 // blur causes a focus change, keep that new focus change,
967 // don't overwrite with the old "currently focused view".
968 nsIView *currentFocusBeforeBlur = GetCurrentlyFocusedView();
970 // Send NS_LOSTFOCUS to widget that was focused before
971 // focus/blur suppression.
972 if (GetViewFocusedBeforeSuppression()) {
973 widget = GetViewFocusedBeforeSuppression()->GetWidget();
974 if (widget) {
975 #ifdef DEBUG_FOCUS_SUPPRESSION
976 printf("*** 0 INFO TODO [CPEARCE] Unsuppressing, dispatching NS_LOSTFOCUS\n");
977 #endif
978 nsGUIEvent event(PR_TRUE, NS_LOSTFOCUS, widget);
979 widget->DispatchEvent(&event, status);
983 // Send NS_GOTFOCUS to the widget that we think should be focused.
984 if (GetCurrentlyFocusedView() &&
985 currentFocusBeforeBlur == GetCurrentlyFocusedView())
987 widget = GetCurrentlyFocusedView()->GetWidget();
988 if (widget) {
989 #ifdef DEBUG_FOCUS_SUPPRESSION
990 printf("*** 0 INFO TODO [CPEARCE] Unsuppressing, dispatching NS_GOTFOCUS\n");
991 #endif
992 nsGUIEvent event(PR_TRUE, NS_GOTFOCUS, widget);
993 widget->DispatchEvent(&event, status);
999 static void ConvertRectAppUnitsToIntPixels(nsRect& aRect, PRInt32 p2a)
1001 aRect.x = NSAppUnitsToIntPixels(aRect.x, p2a);
1002 aRect.y = NSAppUnitsToIntPixels(aRect.y, p2a);
1003 aRect.width = NSAppUnitsToIntPixels(aRect.width, p2a);
1004 aRect.height = NSAppUnitsToIntPixels(aRect.height, p2a);
1007 NS_IMETHODIMP nsViewManager::DispatchEvent(nsGUIEvent *aEvent, nsEventStatus *aStatus)
1009 *aStatus = nsEventStatus_eIgnore;
1011 switch(aEvent->message)
1013 case NS_SIZE:
1015 nsView* view = nsView::GetViewFor(aEvent->widget);
1017 if (nsnull != view)
1019 nscoord width = ((nsSizeEvent*)aEvent)->windowSize->width;
1020 nscoord height = ((nsSizeEvent*)aEvent)->windowSize->height;
1021 width = ((nsSizeEvent*)aEvent)->mWinWidth;
1022 height = ((nsSizeEvent*)aEvent)->mWinHeight;
1024 // The root view may not be set if this is the resize associated with
1025 // window creation
1027 if (view == mRootView)
1029 PRInt32 p2a = mContext->AppUnitsPerDevPixel();
1030 SetWindowDimensions(NSIntPixelsToAppUnits(width, p2a),
1031 NSIntPixelsToAppUnits(height, p2a));
1032 *aStatus = nsEventStatus_eConsumeNoDefault;
1036 break;
1039 case NS_PAINT:
1041 nsPaintEvent *event = static_cast<nsPaintEvent*>(aEvent);
1042 nsView *view = nsView::GetViewFor(aEvent->widget);
1044 if (!view || !mContext)
1045 break;
1047 *aStatus = nsEventStatus_eConsumeNoDefault;
1049 // The rect is in device units, and it's in the coordinate space of its
1050 // associated window.
1051 nsCOMPtr<nsIRegion> region = event->region;
1052 if (!region) {
1053 if (NS_FAILED(CreateRegion(getter_AddRefs(region))))
1054 break;
1056 const nsRect& damrect = *event->rect;
1057 region->SetTo(damrect.x, damrect.y, damrect.width, damrect.height);
1060 if (region->IsEmpty())
1061 break;
1063 // Refresh the view
1064 if (IsRefreshEnabled()) {
1065 // If an ancestor widget was hidden and then shown, we could
1066 // have a delayed resize to handle.
1067 PRBool didResize = PR_FALSE;
1068 for (nsViewManager *vm = this; vm;
1069 vm = vm->mRootView->GetParent()
1070 ? vm->mRootView->GetParent()->GetViewManager()
1071 : nsnull) {
1072 if (vm->mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) &&
1073 IsViewVisible(vm->mRootView)) {
1074 vm->DoSetWindowDimensions(vm->mDelayedResize.width,
1075 vm->mDelayedResize.height);
1076 vm->mDelayedResize.SizeTo(NSCOORD_NONE, NSCOORD_NONE);
1078 // Paint later.
1079 vm->UpdateView(vm->mRootView, NS_VMREFRESH_NO_SYNC);
1080 didResize = PR_TRUE;
1082 // not sure if it's valid for us to claim that we
1083 // ignored this, but we're going to do so anyway, since
1084 // we didn't actually paint anything
1085 *aStatus = nsEventStatus_eIgnore;
1089 if (!didResize) {
1090 //NS_ASSERTION(IsViewVisible(view), "painting an invisible view");
1092 // Just notify our own view observer that we're about to paint
1093 // XXXbz do we need to notify other view observers for viewmanagers
1094 // in our tree?
1095 // Make sure to not send WillPaint notifications while scrolling
1096 nsRefPtr<nsViewManager> rootVM = RootViewManager();
1098 nsIWidget *widget = mRootView->GetWidget();
1099 PRBool transparentWindow = PR_FALSE;
1100 if (widget)
1101 widget->GetHasTransparentBackground(transparentWindow);
1103 if (rootVM->mScrollCnt == 0 && !transparentWindow) {
1104 nsIViewObserver* observer = GetViewObserver();
1105 if (observer) {
1106 // Do an update view batch. Make sure not to do it DEFERRED,
1107 // since that would effectively delay any invalidates that are
1108 // triggered by the WillPaint notification (they'd happen when
1109 // the invalid event fires, which is later than the reflow
1110 // event would fire and could end up being after some timer
1111 // events, leading to frame dropping in DHTML). Note that the
1112 // observer may try to reenter this code from inside
1113 // WillPaint() by trying to do a synchronous paint, but since
1114 // refresh will be disabled it won't be able to do the paint.
1115 // We should really sort out the rules on our synch painting
1116 // api....
1117 UpdateViewBatch batch(this);
1118 observer->WillPaint();
1119 batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
1121 // Get the view pointer again since the code above might have
1122 // destroyed it (bug 378273).
1123 view = nsView::GetViewFor(aEvent->widget);
1126 // Make sure to sync up any widget geometry changes we
1127 // have pending before we paint.
1128 if (rootVM->mHasPendingUpdates) {
1129 rootVM->ProcessPendingUpdates(mRootView, PR_FALSE);
1132 if (view) {
1133 Refresh(view, event->renderingContext, region,
1134 NS_VMREFRESH_DOUBLE_BUFFER);
1137 } else {
1138 // since we got an NS_PAINT event, we need to
1139 // draw something so we don't get blank areas.
1140 nsRect damRect;
1141 region->GetBoundingBox(&damRect.x, &damRect.y, &damRect.width, &damRect.height);
1142 PRInt32 p2a = mContext->AppUnitsPerDevPixel();
1143 damRect.ScaleRoundOut(float(p2a));
1144 DefaultRefresh(view, event->renderingContext, &damRect);
1146 // Clients like the editor can trigger multiple
1147 // reflows during what the user perceives as a single
1148 // edit operation, so it disables view manager
1149 // refreshing until the edit operation is complete
1150 // so that users don't see the intermediate steps.
1152 // Unfortunately some of these reflows can trigger
1153 // nsScrollPortView and nsScrollingView Scroll() calls
1154 // which in most cases force an immediate BitBlt and
1155 // synchronous paint to happen even if the view manager's
1156 // refresh is disabled. (Bug 97674)
1158 // Calling UpdateView() here, is necessary to add
1159 // the exposed region specified in the synchronous paint
1160 // event to the view's damaged region so that it gets
1161 // painted properly when refresh is enabled.
1163 // Note that calling UpdateView() here was deemed
1164 // to have the least impact on performance, since the
1165 // other alternative was to make Scroll() post an
1166 // async paint event for the *entire* ScrollPort or
1167 // ScrollingView's viewable area. (See bug 97674 for this
1168 // alternate patch.)
1170 UpdateView(view, damRect, NS_VMREFRESH_NO_SYNC);
1173 break;
1176 case NS_CREATE:
1177 case NS_DESTROY:
1178 case NS_SETZLEVEL:
1179 case NS_MOVE:
1180 /* Don't pass these events through. Passing them through
1181 causes performance problems on pages with lots of views/frames
1182 @see bug 112861 */
1183 *aStatus = nsEventStatus_eConsumeNoDefault;
1184 break;
1187 case NS_DISPLAYCHANGED:
1189 //Destroy the cached backbuffer to force a new backbuffer
1190 //be constructed with the appropriate display depth.
1191 //@see bugzilla bug 6061
1192 *aStatus = nsEventStatus_eConsumeDoDefault;
1193 break;
1195 case NS_SYSCOLORCHANGED:
1197 // Hold a refcount to the observer. The continued existence of the observer will
1198 // delay deletion of this view hierarchy should the event want to cause its
1199 // destruction in, say, some JavaScript event handler.
1200 nsView *view = nsView::GetViewFor(aEvent->widget);
1201 nsCOMPtr<nsIViewObserver> obs = GetViewObserver();
1202 if (obs) {
1203 obs->HandleEvent(view, aEvent, aStatus);
1206 break;
1208 default:
1210 if (aEvent->message == NS_GOTFOCUS) {
1211 #ifdef DEBUG_FOCUS_SUPPRESSION
1212 printf("*** 0 INFO TODO [CPEARCE] Focus changing%s\n",
1213 (nsViewManager::IsFocusSuppressed() ? " while suppressed" : ""));
1214 #endif
1215 SetCurrentlyFocusedView(nsView::GetViewFor(aEvent->widget));
1217 if ((aEvent->message == NS_GOTFOCUS || aEvent->message == NS_LOSTFOCUS) &&
1218 nsViewManager::IsFocusSuppressed())
1220 #ifdef DEBUG_FOCUS_SUPPRESSION
1221 printf("*** 0 INFO TODO [CPEARCE] Suppressing %s\n",
1222 (aEvent->message == NS_GOTFOCUS ? "NS_GOTFOCUS" : "NS_LOSTFOCUS"));
1223 #endif
1224 break;
1227 if ((NS_IS_MOUSE_EVENT(aEvent) &&
1228 // Ignore moves that we synthesize.
1229 static_cast<nsMouseEvent*>(aEvent)->reason ==
1230 nsMouseEvent::eReal &&
1231 // Ignore mouse exit and enter (we'll get moves if the user
1232 // is really moving the mouse) since we get them when we
1233 // create and destroy widgets.
1234 aEvent->message != NS_MOUSE_EXIT &&
1235 aEvent->message != NS_MOUSE_ENTER) ||
1236 NS_IS_KEY_EVENT(aEvent) ||
1237 NS_IS_IME_EVENT(aEvent)) {
1238 gLastUserEventTime = PR_IntervalToMicroseconds(PR_IntervalNow());
1241 if (aEvent->message == NS_DEACTIVATE) {
1242 PRBool result;
1243 GrabMouseEvents(nsnull, result);
1246 //Find the view whose coordinates system we're in.
1247 nsView* baseView = nsView::GetViewFor(aEvent->widget);
1248 nsView* view = baseView;
1249 PRBool capturedEvent = PR_FALSE;
1251 if (!NS_IS_KEY_EVENT(aEvent) && !NS_IS_IME_EVENT(aEvent) &&
1252 !NS_IS_CONTEXT_MENU_KEY(aEvent) && !NS_IS_FOCUS_EVENT(aEvent) &&
1253 !NS_IS_QUERY_CONTENT_EVENT(aEvent) &&
1254 aEvent->eventStructType != NS_ACCESSIBLE_EVENT) {
1255 // will dispatch using coordinates. Pretty bogus but it's consistent
1256 // with what presshell does.
1257 view = GetDisplayRootFor(baseView);
1260 //Find the view to which we're initially going to send the event
1261 //for hittesting.
1262 if (NS_IS_MOUSE_EVENT(aEvent) || NS_IS_DRAG_EVENT(aEvent)) {
1263 nsView* mouseGrabber = GetMouseEventGrabber();
1264 if (mouseGrabber) {
1265 view = mouseGrabber;
1266 capturedEvent = PR_TRUE;
1270 if (nsnull != view) {
1271 PRInt32 p2a = mContext->AppUnitsPerDevPixel();
1273 if ((aEvent->message == NS_MOUSE_MOVE &&
1274 static_cast<nsMouseEvent*>(aEvent)->reason ==
1275 nsMouseEvent::eReal) ||
1276 aEvent->message == NS_MOUSE_ENTER) {
1277 // aEvent->point is relative to the widget, i.e. the view top-left,
1278 // so we need to add the offset to the view origin
1279 nsPoint rootOffset = baseView->GetDimensions().TopLeft();
1280 rootOffset += baseView->GetOffsetTo(RootViewManager()->mRootView);
1281 RootViewManager()->mMouseLocation = aEvent->refPoint +
1282 nsPoint(NSAppUnitsToIntPixels(rootOffset.x, p2a),
1283 NSAppUnitsToIntPixels(rootOffset.y, p2a));
1284 #ifdef DEBUG_MOUSE_LOCATION
1285 if (aEvent->message == NS_MOUSE_ENTER)
1286 printf("[vm=%p]got mouse enter for %p\n",
1287 this, aEvent->widget);
1288 printf("[vm=%p]setting mouse location to (%d,%d)\n",
1289 this, mMouseLocation.x, mMouseLocation.y);
1290 #endif
1291 if (aEvent->message == NS_MOUSE_ENTER)
1292 SynthesizeMouseMove(PR_FALSE);
1293 } else if (aEvent->message == NS_MOUSE_EXIT) {
1294 // Although we only care about the mouse moving into an area
1295 // for which this view manager doesn't receive mouse move
1296 // events, we don't check which view the mouse exit was for
1297 // since this seems to vary by platform. Hopefully this
1298 // won't matter at all since we'll get the mouse move or
1299 // enter after the mouse exit when the mouse moves from one
1300 // of our widgets into another.
1301 RootViewManager()->mMouseLocation = nsPoint(NSCOORD_NONE, NSCOORD_NONE);
1302 #ifdef DEBUG_MOUSE_LOCATION
1303 printf("[vm=%p]got mouse exit for %p\n",
1304 this, aEvent->widget);
1305 printf("[vm=%p]clearing mouse location\n",
1306 this);
1307 #endif
1310 //Calculate the proper offset for the view we're going to
1311 nsPoint offset(0, 0);
1313 if (view != baseView) {
1314 //Get offset from root of baseView
1315 nsView *parent;
1316 for (parent = baseView; parent; parent = parent->GetParent())
1317 parent->ConvertToParentCoords(&offset.x, &offset.y);
1319 //Subtract back offset from root of view
1320 for (parent = view; parent; parent = parent->GetParent())
1321 parent->ConvertFromParentCoords(&offset.x, &offset.y);
1324 // Dispatch the event
1325 nsRect baseViewDimensions;
1326 if (baseView != nsnull) {
1327 baseView->GetDimensions(baseViewDimensions);
1330 nsPoint pt;
1331 pt.x = baseViewDimensions.x +
1332 NSFloatPixelsToAppUnits(float(aEvent->refPoint.x) + 0.5f, p2a);
1333 pt.y = baseViewDimensions.y +
1334 NSFloatPixelsToAppUnits(float(aEvent->refPoint.y) + 0.5f, p2a);
1335 pt += offset;
1337 *aStatus = HandleEvent(view, pt, aEvent, capturedEvent);
1340 // need to map the reply back into platform coordinates
1342 switch (aEvent->message) {
1343 case NS_TEXT_TEXT:
1344 ConvertRectAppUnitsToIntPixels(
1345 ((nsTextEvent*)aEvent)->theReply.mCursorPosition, p2a);
1346 break;
1347 case NS_COMPOSITION_START:
1348 case NS_COMPOSITION_QUERY:
1349 ConvertRectAppUnitsToIntPixels(
1350 ((nsCompositionEvent*)aEvent)->theReply.mCursorPosition, p2a);
1351 break;
1352 case NS_QUERYCARETRECT:
1353 ConvertRectAppUnitsToIntPixels(
1354 ((nsQueryCaretRectEvent*)aEvent)->theReply.mCaretRect, p2a);
1355 break;
1356 case NS_QUERY_CHARACTER_RECT:
1357 case NS_QUERY_CARET_RECT:
1358 ConvertRectAppUnitsToIntPixels(
1359 ((nsQueryContentEvent*)aEvent)->mReply.mRect, p2a);
1360 break;
1364 break;
1368 return NS_OK;
1371 nsEventStatus nsViewManager::HandleEvent(nsView* aView, nsPoint aPoint,
1372 nsGUIEvent* aEvent, PRBool aCaptured) {
1373 //printf(" %d %d %d %d (%d,%d) \n", this, event->widget, event->widgetSupports,
1374 // event->message, event->point.x, event->point.y);
1376 // Hold a refcount to the observer. The continued existence of the observer will
1377 // delay deletion of this view hierarchy should the event want to cause its
1378 // destruction in, say, some JavaScript event handler.
1379 nsCOMPtr<nsIViewObserver> obs = aView->GetViewManager()->GetViewObserver();
1380 nsEventStatus status = nsEventStatus_eIgnore;
1381 if (obs) {
1382 obs->HandleEvent(aView, aEvent, &status);
1385 return status;
1388 NS_IMETHODIMP nsViewManager::GrabMouseEvents(nsIView *aView, PRBool &aResult)
1390 if (!IsRootVM()) {
1391 return RootViewManager()->GrabMouseEvents(aView, aResult);
1394 // Along with nsView::SetVisibility, we enforce that the mouse grabber
1395 // can never be a hidden view.
1396 if (aView && static_cast<nsView*>(aView)->GetVisibility()
1397 == nsViewVisibility_kHide) {
1398 aView = nsnull;
1401 #ifdef DEBUG_mjudge
1402 if (aView)
1404 printf("capturing mouse events for view %x\n",aView);
1406 printf("removing mouse capture from view %x\n",mMouseGrabber);
1407 #endif
1409 mMouseGrabber = static_cast<nsView*>(aView);
1410 aResult = PR_TRUE;
1411 return NS_OK;
1414 NS_IMETHODIMP nsViewManager::GetMouseEventGrabber(nsIView *&aView)
1416 aView = GetMouseEventGrabber();
1417 return NS_OK;
1420 // Recursively reparent widgets if necessary
1422 void nsViewManager::ReparentChildWidgets(nsIView* aView, nsIWidget *aNewWidget)
1424 if (aView->HasWidget()) {
1425 // Check to see if the parent widget is the
1426 // same as the new parent. If not then reparent
1427 // the widget, otherwise there is nothing more
1428 // to do for the view and its descendants
1429 nsIWidget* widget = aView->GetWidget();
1430 nsIWidget* parentWidget = widget->GetParent();
1431 // Toplevel widgets should not be reparented!
1432 if (parentWidget && parentWidget != aNewWidget) {
1433 #ifdef DEBUG
1434 nsresult rv =
1435 #endif
1436 widget->SetParent(aNewWidget);
1437 NS_ASSERTION(NS_SUCCEEDED(rv), "SetParent failed!");
1439 return;
1442 // Need to check each of the views children to see
1443 // if they have a widget and reparent it.
1445 nsView* view = static_cast<nsView*>(aView);
1446 for (nsView *kid = view->GetFirstChild(); kid; kid = kid->GetNextSibling()) {
1447 ReparentChildWidgets(kid, aNewWidget);
1451 // Reparent a view and its descendant views widgets if necessary
1453 void nsViewManager::ReparentWidgets(nsIView* aView, nsIView *aParent)
1455 NS_PRECONDITION(aParent, "Must have a parent");
1456 NS_PRECONDITION(aView, "Must have a view");
1458 // Quickly determine whether the view has pre-existing children or a
1459 // widget. In most cases the view will not have any pre-existing
1460 // children when this is called. Only in the case
1461 // where a view has been reparented by removing it from
1462 // a reinserting it into a new location in the view hierarchy do we
1463 // have to consider reparenting the existing widgets for the view and
1464 // it's descendants.
1465 nsView* view = static_cast<nsView*>(aView);
1466 if (view->HasWidget() || view->GetFirstChild()) {
1467 nsIWidget* parentWidget = aParent->GetNearestWidget(nsnull);
1468 if (parentWidget) {
1469 ReparentChildWidgets(aView, parentWidget);
1470 return;
1472 NS_WARNING("Can not find a widget for the parent view");
1476 NS_IMETHODIMP nsViewManager::InsertChild(nsIView *aParent, nsIView *aChild, nsIView *aSibling,
1477 PRBool aAfter)
1479 nsView* parent = static_cast<nsView*>(aParent);
1480 nsView* child = static_cast<nsView*>(aChild);
1481 nsView* sibling = static_cast<nsView*>(aSibling);
1483 NS_PRECONDITION(nsnull != parent, "null ptr");
1484 NS_PRECONDITION(nsnull != child, "null ptr");
1485 NS_ASSERTION(sibling == nsnull || sibling->GetParent() == parent,
1486 "tried to insert view with invalid sibling");
1487 NS_ASSERTION(!IsViewInserted(child), "tried to insert an already-inserted view");
1489 if ((nsnull != parent) && (nsnull != child))
1491 // if aAfter is set, we will insert the child after 'prev' (i.e. after 'kid' in document
1492 // order, otherwise after 'kid' (i.e. before 'kid' in document order).
1494 #if 1
1495 if (nsnull == aSibling) {
1496 if (aAfter) {
1497 // insert at end of document order, i.e., before first view
1498 // this is the common case, by far
1499 parent->InsertChild(child, nsnull);
1500 ReparentWidgets(child, parent);
1501 } else {
1502 // insert at beginning of document order, i.e., after last view
1503 nsView *kid = parent->GetFirstChild();
1504 nsView *prev = nsnull;
1505 while (kid) {
1506 prev = kid;
1507 kid = kid->GetNextSibling();
1509 // prev is last view or null if there are no children
1510 parent->InsertChild(child, prev);
1511 ReparentWidgets(child, parent);
1513 } else {
1514 nsView *kid = parent->GetFirstChild();
1515 nsView *prev = nsnull;
1516 while (kid && sibling != kid) {
1517 //get the next sibling view
1518 prev = kid;
1519 kid = kid->GetNextSibling();
1521 NS_ASSERTION(kid != nsnull,
1522 "couldn't find sibling in child list");
1523 if (aAfter) {
1524 // insert after 'kid' in document order, i.e. before in view order
1525 parent->InsertChild(child, prev);
1526 ReparentWidgets(child, parent);
1527 } else {
1528 // insert before 'kid' in document order, i.e. after in view order
1529 parent->InsertChild(child, kid);
1530 ReparentWidgets(child, parent);
1533 #else // don't keep consistent document order, but order things by z-index instead
1534 // essentially we're emulating the old InsertChild(parent, child, zindex)
1535 PRInt32 zIndex = child->GetZIndex();
1536 while (nsnull != kid)
1538 PRInt32 idx = kid->GetZIndex();
1540 if (CompareZIndex(zIndex, child->IsTopMost(), child->GetZIndexIsAuto(),
1541 idx, kid->IsTopMost(), kid->GetZIndexIsAuto()) >= 0)
1542 break;
1544 prev = kid;
1545 kid = kid->GetNextSibling();
1548 parent->InsertChild(child, prev);
1549 ReparentWidgets(child, parent);
1550 #endif
1552 // if the parent view is marked as "floating", make the newly added view float as well.
1553 if (parent->GetFloating())
1554 child->SetFloating(PR_TRUE);
1556 //and mark this area as dirty if the view is visible...
1558 if (nsViewVisibility_kHide != child->GetVisibility())
1559 UpdateView(child, NS_VMREFRESH_NO_SYNC);
1561 return NS_OK;
1564 NS_IMETHODIMP nsViewManager::InsertChild(nsIView *aParent, nsIView *aChild, PRInt32 aZIndex)
1566 // no-one really calls this with anything other than aZIndex == 0 on a fresh view
1567 // XXX this method should simply be eliminated and its callers redirected to the real method
1568 SetViewZIndex(aChild, PR_FALSE, aZIndex, PR_FALSE);
1569 return InsertChild(aParent, aChild, nsnull, PR_TRUE);
1572 NS_IMETHODIMP nsViewManager::RemoveChild(nsIView *aChild)
1574 nsView* child = static_cast<nsView*>(aChild);
1575 NS_ENSURE_ARG_POINTER(child);
1577 nsView* parent = child->GetParent();
1579 if (nsnull != parent)
1581 UpdateView(child, NS_VMREFRESH_NO_SYNC);
1582 parent->RemoveChild(child);
1585 return NS_OK;
1588 NS_IMETHODIMP nsViewManager::MoveViewBy(nsIView *aView, nscoord aX, nscoord aY)
1590 nsView* view = static_cast<nsView*>(aView);
1592 nsPoint pt = view->GetPosition();
1593 MoveViewTo(view, aX + pt.x, aY + pt.y);
1594 return NS_OK;
1597 NS_IMETHODIMP nsViewManager::MoveViewTo(nsIView *aView, nscoord aX, nscoord aY)
1599 nsView* view = static_cast<nsView*>(aView);
1600 nsPoint oldPt = view->GetPosition();
1601 nsRect oldArea = view->GetBounds();
1602 view->SetPosition(aX, aY);
1604 // only do damage control if the view is visible
1606 if ((aX != oldPt.x) || (aY != oldPt.y)) {
1607 if (view->GetVisibility() != nsViewVisibility_kHide) {
1608 nsView* parentView = view->GetParent();
1609 UpdateView(parentView, oldArea, NS_VMREFRESH_NO_SYNC);
1610 UpdateView(parentView, view->GetBounds(), NS_VMREFRESH_NO_SYNC);
1613 return NS_OK;
1616 void nsViewManager::InvalidateHorizontalBandDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut,
1617 PRUint32 aUpdateFlags, nscoord aY1, nscoord aY2, PRBool aInCutOut) {
1618 nscoord height = aY2 - aY1;
1619 if (aRect.x < aCutOut.x) {
1620 nsRect r(aRect.x, aY1, aCutOut.x - aRect.x, height);
1621 UpdateView(aView, r, aUpdateFlags);
1623 if (!aInCutOut && aCutOut.x < aCutOut.XMost()) {
1624 nsRect r(aCutOut.x, aY1, aCutOut.width, height);
1625 UpdateView(aView, r, aUpdateFlags);
1627 if (aCutOut.XMost() < aRect.XMost()) {
1628 nsRect r(aCutOut.XMost(), aY1, aRect.XMost() - aCutOut.XMost(), height);
1629 UpdateView(aView, r, aUpdateFlags);
1633 void nsViewManager::InvalidateRectDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut,
1634 PRUint32 aUpdateFlags) {
1635 if (aRect.y < aCutOut.y) {
1636 InvalidateHorizontalBandDifference(aView, aRect, aCutOut, aUpdateFlags, aRect.y, aCutOut.y, PR_FALSE);
1638 if (aCutOut.y < aCutOut.YMost()) {
1639 InvalidateHorizontalBandDifference(aView, aRect, aCutOut, aUpdateFlags, aCutOut.y, aCutOut.YMost(), PR_TRUE);
1641 if (aCutOut.YMost() < aRect.YMost()) {
1642 InvalidateHorizontalBandDifference(aView, aRect, aCutOut, aUpdateFlags, aCutOut.YMost(), aRect.YMost(), PR_FALSE);
1646 NS_IMETHODIMP nsViewManager::ResizeView(nsIView *aView, const nsRect &aRect, PRBool aRepaintExposedAreaOnly)
1648 nsView* view = static_cast<nsView*>(aView);
1649 nsRect oldDimensions;
1651 view->GetDimensions(oldDimensions);
1652 if (!oldDimensions.IsExactEqual(aRect)) {
1653 nsView* parentView = view->GetParent();
1654 if (parentView == nsnull)
1655 parentView = view;
1657 // resize the view.
1658 // Prevent Invalidation of hidden views
1659 if (view->GetVisibility() == nsViewVisibility_kHide) {
1660 view->SetDimensions(aRect, PR_FALSE);
1661 } else {
1662 if (!aRepaintExposedAreaOnly) {
1663 //Invalidate the union of the old and new size
1664 view->SetDimensions(aRect, PR_TRUE);
1666 UpdateView(view, aRect, NS_VMREFRESH_NO_SYNC);
1667 view->ConvertToParentCoords(&oldDimensions.x, &oldDimensions.y);
1668 UpdateView(parentView, oldDimensions, NS_VMREFRESH_NO_SYNC);
1669 } else {
1670 view->SetDimensions(aRect, PR_TRUE);
1672 InvalidateRectDifference(view, aRect, oldDimensions, NS_VMREFRESH_NO_SYNC);
1673 nsRect r = aRect;
1674 view->ConvertToParentCoords(&r.x, &r.y);
1675 view->ConvertToParentCoords(&oldDimensions.x, &oldDimensions.y);
1676 InvalidateRectDifference(parentView, oldDimensions, r, NS_VMREFRESH_NO_SYNC);
1681 // Note that if layout resizes the view and the view has a custom clip
1682 // region set, then we expect layout to update the clip region too. Thus
1683 // in the case where mClipRect has been optimized away to just be a null
1684 // pointer, and this resize is implicitly changing the clip rect, it's OK
1685 // because layout will change it back again if necessary.
1687 return NS_OK;
1690 PRBool nsViewManager::CanScrollWithBitBlt(nsView* aView, nsPoint aDelta,
1691 nsRegion* aUpdateRegion)
1693 NS_ASSERTION(!IsPainting(),
1694 "View manager shouldn't be scrolling during a paint");
1695 if (IsPainting() || !mObserver) {
1696 return PR_FALSE; // do the safe thing
1699 nsView* displayRoot = GetDisplayRootFor(aView);
1700 nsPoint displayOffset = aView->GetParent()->GetOffsetTo(displayRoot);
1701 nsRect parentBounds = aView->GetParent()->GetDimensions() + displayOffset;
1702 // The rect we're going to scroll is intersection of the parent bounds with its
1703 // preimage
1704 nsRect toScroll;
1705 toScroll.IntersectRect(parentBounds + aDelta, parentBounds);
1706 nsresult rv =
1707 mObserver->ComputeRepaintRegionForCopy(displayRoot, aView, -aDelta, toScroll,
1708 aUpdateRegion);
1709 NS_ENSURE_SUCCESS(rv, PR_FALSE);
1711 aUpdateRegion->MoveBy(-displayOffset);
1713 #if defined(MOZ_WIDGET_GTK2) || defined(XP_OS2)
1714 return aUpdateRegion->IsEmpty();
1715 #else
1716 return PR_TRUE;
1717 #endif
1720 NS_IMETHODIMP nsViewManager::SetViewFloating(nsIView *aView, PRBool aFloating)
1722 nsView* view = static_cast<nsView*>(aView);
1724 NS_ASSERTION(!(nsnull == view), "no view");
1726 view->SetFloating(aFloating);
1728 return NS_OK;
1731 NS_IMETHODIMP nsViewManager::SetViewVisibility(nsIView *aView, nsViewVisibility aVisible)
1733 nsView* view = static_cast<nsView*>(aView);
1735 if (aVisible != view->GetVisibility()) {
1736 view->SetVisibility(aVisible);
1738 if (IsViewInserted(view)) {
1739 if (!view->HasWidget()) {
1740 if (nsViewVisibility_kHide == aVisible) {
1741 nsView* parentView = view->GetParent();
1742 if (parentView) {
1743 UpdateView(parentView, view->GetBounds(), NS_VMREFRESH_NO_SYNC);
1746 else {
1747 UpdateView(view, NS_VMREFRESH_NO_SYNC);
1752 // Any child views not associated with frames might not get their visibility
1753 // updated, so propagate our visibility to them. This is important because
1754 // hidden views should have all hidden children.
1755 for (nsView* childView = view->GetFirstChild(); childView;
1756 childView = childView->GetNextSibling()) {
1757 if (!childView->GetClientData()) {
1758 childView->SetVisibility(aVisible);
1762 return NS_OK;
1765 void nsViewManager::UpdateWidgetsForView(nsView* aView)
1767 NS_PRECONDITION(aView, "Must have view!");
1769 if (aView->HasWidget()) {
1770 aView->GetWidget()->Update();
1773 for (nsView* childView = aView->GetFirstChild();
1774 childView;
1775 childView = childView->GetNextSibling()) {
1776 UpdateWidgetsForView(childView);
1780 PRBool nsViewManager::IsViewInserted(nsView *aView)
1782 if (mRootView == aView) {
1783 return PR_TRUE;
1784 } else if (aView->GetParent() == nsnull) {
1785 return PR_FALSE;
1786 } else {
1787 nsView* view = aView->GetParent()->GetFirstChild();
1788 while (view != nsnull) {
1789 if (view == aView) {
1790 return PR_TRUE;
1792 view = view->GetNextSibling();
1794 return PR_FALSE;
1798 NS_IMETHODIMP nsViewManager::SetViewZIndex(nsIView *aView, PRBool aAutoZIndex, PRInt32 aZIndex, PRBool aTopMost)
1800 nsView* view = static_cast<nsView*>(aView);
1801 nsresult rv = NS_OK;
1803 NS_ASSERTION((view != nsnull), "no view");
1805 // don't allow the root view's z-index to be changed. It should always be zero.
1806 // This could be removed and replaced with a style rule, or just removed altogether, with interesting consequences
1807 if (aView == mRootView) {
1808 return rv;
1811 PRBool oldTopMost = view->IsTopMost();
1812 PRBool oldIsAuto = view->GetZIndexIsAuto();
1814 if (aAutoZIndex) {
1815 aZIndex = 0;
1818 PRInt32 oldidx = view->GetZIndex();
1819 view->SetZIndex(aAutoZIndex, aZIndex, aTopMost);
1821 if (oldidx != aZIndex || oldTopMost != aTopMost ||
1822 oldIsAuto != aAutoZIndex) {
1823 UpdateView(view, NS_VMREFRESH_NO_SYNC);
1826 return rv;
1829 NS_IMETHODIMP nsViewManager::SetViewObserver(nsIViewObserver *aObserver)
1831 mObserver = aObserver;
1832 return NS_OK;
1835 NS_IMETHODIMP nsViewManager::GetViewObserver(nsIViewObserver *&aObserver)
1837 if (nsnull != mObserver) {
1838 aObserver = mObserver;
1839 NS_ADDREF(mObserver);
1840 return NS_OK;
1841 } else
1842 return NS_ERROR_NO_INTERFACE;
1845 NS_IMETHODIMP nsViewManager::GetDeviceContext(nsIDeviceContext *&aContext)
1847 NS_IF_ADDREF(mContext);
1848 aContext = mContext;
1849 return NS_OK;
1852 already_AddRefed<nsIRenderingContext>
1853 nsViewManager::CreateRenderingContext(nsView &aView)
1855 nsView *par = &aView;
1856 nsIWidget* win;
1857 nsIRenderingContext *cx = nsnull;
1858 nscoord ax = 0, ay = 0;
1862 win = par->GetWidget();
1863 if (win)
1864 break;
1866 //get absolute coordinates of view, but don't
1867 //add in view pos since the first thing you ever
1868 //need to do when painting a view is to translate
1869 //the rendering context by the views pos and other parts
1870 //of the code do this for us...
1872 if (par != &aView)
1874 par->ConvertToParentCoords(&ax, &ay);
1877 par = par->GetParent();
1879 while (nsnull != par);
1881 if (nsnull != win)
1883 mContext->CreateRenderingContext(par, cx);
1885 if (nsnull != cx)
1886 cx->Translate(ax, ay);
1889 return cx;
1892 NS_IMETHODIMP nsViewManager::DisableRefresh(void)
1894 if (!IsRootVM()) {
1895 return RootViewManager()->DisableRefresh();
1898 if (mUpdateBatchCnt > 0)
1899 return NS_OK;
1901 mRefreshEnabled = PR_FALSE;
1902 return NS_OK;
1905 NS_IMETHODIMP nsViewManager::EnableRefresh(PRUint32 aUpdateFlags)
1907 if (!IsRootVM()) {
1908 return RootViewManager()->EnableRefresh(aUpdateFlags);
1911 if (mUpdateBatchCnt > 0)
1912 return NS_OK;
1914 mRefreshEnabled = PR_TRUE;
1916 if (!mHasPendingUpdates) {
1917 // Nothing to do
1918 return NS_OK;
1921 // nested batching can combine IMMEDIATE with DEFERRED. Favour
1922 // IMMEDIATE over DEFERRED and DEFERRED over NO_SYNC.
1923 if (aUpdateFlags & NS_VMREFRESH_IMMEDIATE) {
1924 FlushPendingInvalidates();
1925 Composite();
1926 } else if (aUpdateFlags & NS_VMREFRESH_DEFERRED) {
1927 PostInvalidateEvent();
1928 } else { // NO_SYNC
1929 FlushPendingInvalidates();
1932 return NS_OK;
1935 nsIViewManager* nsViewManager::BeginUpdateViewBatch(void)
1937 if (!IsRootVM()) {
1938 return RootViewManager()->BeginUpdateViewBatch();
1941 nsresult result = NS_OK;
1943 if (mUpdateBatchCnt == 0) {
1944 mUpdateBatchFlags = 0;
1945 result = DisableRefresh();
1948 if (NS_SUCCEEDED(result))
1949 ++mUpdateBatchCnt;
1951 return this;
1954 NS_IMETHODIMP nsViewManager::EndUpdateViewBatch(PRUint32 aUpdateFlags)
1956 NS_ASSERTION(IsRootVM(), "Should only be called on root");
1958 nsresult result = NS_OK;
1960 --mUpdateBatchCnt;
1962 NS_ASSERTION(mUpdateBatchCnt >= 0, "Invalid batch count!");
1964 if (mUpdateBatchCnt < 0)
1966 mUpdateBatchCnt = 0;
1967 return NS_ERROR_FAILURE;
1970 mUpdateBatchFlags |= aUpdateFlags;
1971 if (mUpdateBatchCnt == 0) {
1972 result = EnableRefresh(mUpdateBatchFlags);
1975 return result;
1978 NS_IMETHODIMP nsViewManager::SetRootScrollableView(nsIScrollableView *aScrollable)
1980 mRootScrollable = aScrollable;
1981 return NS_OK;
1984 NS_IMETHODIMP nsViewManager::GetRootScrollableView(nsIScrollableView **aScrollable)
1986 *aScrollable = mRootScrollable;
1987 return NS_OK;
1990 NS_IMETHODIMP nsViewManager::GetWidget(nsIWidget **aWidget)
1992 *aWidget = GetWidget();
1993 NS_IF_ADDREF(*aWidget);
1994 return NS_OK;
1997 NS_IMETHODIMP nsViewManager::ForceUpdate()
1999 if (!IsRootVM()) {
2000 return RootViewManager()->ForceUpdate();
2003 // Walk the view tree looking for widgets, and call Update() on each one
2004 if (mRootView) {
2005 UpdateWidgetsForView(mRootView);
2008 return NS_OK;
2011 nsPoint nsViewManager::ComputeViewOffset(const nsView *aView)
2013 NS_PRECONDITION(aView, "Null view in ComputeViewOffset?");
2015 nsPoint origin(0, 0);
2016 #ifdef DEBUG
2017 const nsView* rootView;
2018 const nsView* origView = aView;
2019 #endif
2021 while (aView) {
2022 #ifdef DEBUG
2023 rootView = aView;
2024 #endif
2025 origin += aView->GetPosition();
2026 aView = aView->GetParent();
2028 NS_ASSERTION(rootView ==
2029 origView->GetViewManager()->RootViewManager()->GetRootView(),
2030 "Unexpected root view");
2031 return origin;
2034 void nsViewManager::ViewToWidget(nsView *aView, nsView* aWidgetView, nsRect &aRect) const
2036 while (aView != aWidgetView) {
2037 aView->ConvertToParentCoords(&aRect.x, &aRect.y);
2038 aView = aView->GetParent();
2041 // intersect aRect with bounds of aWidgetView, to prevent generating any illegal rectangles.
2042 nsRect bounds;
2043 aWidgetView->GetDimensions(bounds);
2044 aRect.IntersectRect(aRect, bounds);
2045 // account for the view's origin not lining up with the widget's
2046 aRect.x -= bounds.x;
2047 aRect.y -= bounds.y;
2049 aRect += aView->ViewToWidgetOffset();
2051 // finally, convert to device coordinates.
2052 aRect.ScaleRoundOut(1.0f / mContext->AppUnitsPerDevPixel());
2055 nsresult nsViewManager::GetVisibleRect(nsRect& aVisibleRect)
2057 nsresult rv = NS_OK;
2059 // Get the viewport scroller
2060 nsIScrollableView* scrollingView;
2061 GetRootScrollableView(&scrollingView);
2063 if (scrollingView) {
2064 // Determine the visible rect in the scrolled view's coordinate space.
2065 // The size of the visible area is the clip view size
2066 nsScrollPortView* clipView = static_cast<nsScrollPortView*>(scrollingView);
2067 clipView->GetDimensions(aVisibleRect);
2069 scrollingView->GetScrollPosition(aVisibleRect.x, aVisibleRect.y);
2070 } else {
2071 rv = NS_ERROR_FAILURE;
2074 return rv;
2077 nsresult nsViewManager::GetAbsoluteRect(nsView *aView, const nsRect &aRect,
2078 nsRect& aAbsRect)
2080 nsIScrollableView* scrollingView = nsnull;
2081 GetRootScrollableView(&scrollingView);
2082 if (nsnull == scrollingView) {
2083 return NS_ERROR_FAILURE;
2086 nsIView* scrolledIView = nsnull;
2087 scrollingView->GetScrolledView(scrolledIView);
2089 nsView* scrolledView = static_cast<nsView*>(scrolledIView);
2091 // Calculate the absolute coordinates of the aRect passed in.
2092 // aRects values are relative to aView
2093 aAbsRect = aRect;
2094 nsView *parentView = aView;
2095 while ((parentView != nsnull) && (parentView != scrolledView)) {
2096 parentView->ConvertToParentCoords(&aAbsRect.x, &aAbsRect.y);
2097 parentView = parentView->GetParent();
2100 if (parentView != scrolledView) {
2101 return NS_ERROR_FAILURE;
2104 return NS_OK;
2108 NS_IMETHODIMP nsViewManager::GetRectVisibility(nsIView *aView,
2109 const nsRect &aRect,
2110 PRUint16 aMinTwips,
2111 nsRectVisibility *aRectVisibility)
2113 nsView* view = static_cast<nsView*>(aView);
2115 // The parameter aMinTwips determines how many rows/cols of pixels must be visible on each side of the element,
2116 // in order to be counted as visible
2118 *aRectVisibility = nsRectVisibility_kZeroAreaRect;
2119 if (aRect.width == 0 || aRect.height == 0) {
2120 return NS_OK;
2123 // is this view even visible?
2124 if (view->GetVisibility() == nsViewVisibility_kHide) {
2125 return NS_OK;
2128 // nsViewManager::InsertChild ensures that descendants of floating views
2129 // are also marked floating.
2130 if (view->GetFloating()) {
2131 *aRectVisibility = nsRectVisibility_kVisible;
2132 return NS_OK;
2135 // Calculate the absolute coordinates for the visible rectangle
2136 nsRect visibleRect;
2137 if (GetVisibleRect(visibleRect) == NS_ERROR_FAILURE) {
2138 *aRectVisibility = nsRectVisibility_kVisible;
2139 return NS_OK;
2142 // Calculate the absolute coordinates of the aRect passed in.
2143 // aRects values are relative to aView
2144 nsRect absRect;
2145 if ((GetAbsoluteRect(view, aRect, absRect)) == NS_ERROR_FAILURE) {
2146 *aRectVisibility = nsRectVisibility_kVisible;
2147 return NS_OK;
2151 * If aMinTwips > 0, ensure at least aMinTwips of space around object is visible
2152 * The object is not visible if:
2153 * ((objectTop < windowTop && objectBottom < windowTop) ||
2154 * (objectBottom > windowBottom && objectTop > windowBottom) ||
2155 * (objectLeft < windowLeft && objectRight < windowLeft) ||
2156 * (objectRight > windowRight && objectLeft > windowRight))
2159 if (absRect.y < visibleRect.y &&
2160 absRect.y + absRect.height < visibleRect.y + aMinTwips)
2161 *aRectVisibility = nsRectVisibility_kAboveViewport;
2162 else if (absRect.y + absRect.height > visibleRect.y + visibleRect.height &&
2163 absRect.y > visibleRect.y + visibleRect.height - aMinTwips)
2164 *aRectVisibility = nsRectVisibility_kBelowViewport;
2165 else if (absRect.x < visibleRect.x &&
2166 absRect.x + absRect.width < visibleRect.x + aMinTwips)
2167 *aRectVisibility = nsRectVisibility_kLeftOfViewport;
2168 else if (absRect.x + absRect.width > visibleRect.x + visibleRect.width &&
2169 absRect.x > visibleRect.x + visibleRect.width - aMinTwips)
2170 *aRectVisibility = nsRectVisibility_kRightOfViewport;
2171 else
2172 *aRectVisibility = nsRectVisibility_kVisible;
2174 return NS_OK;
2177 NS_IMETHODIMP
2178 nsViewManager::IsPainting(PRBool& aIsPainting)
2180 aIsPainting = IsPainting();
2181 return NS_OK;
2184 void
2185 nsViewManager::FlushPendingInvalidates()
2187 NS_ASSERTION(IsRootVM(), "Must be root VM for this to be called!\n");
2188 NS_ASSERTION(mUpdateBatchCnt == 0, "Must not be in an update batch!");
2189 // XXXbz this is probably not quite OK yet, if callers can explicitly
2190 // DisableRefresh while we have an event posted.
2191 // NS_ASSERTION(mRefreshEnabled, "How did we get here?");
2193 // Let all the view observers of all viewmanagers in this tree know that
2194 // we're about to "paint" (this lets them get in their invalidates now so
2195 // we don't go through two invalidate-processing cycles).
2196 NS_ASSERTION(gViewManagers, "Better have a viewmanagers array!");
2198 // Make sure to not send WillPaint notifications while scrolling
2199 if (mScrollCnt == 0) {
2200 // Disable refresh while we notify our view observers, so that if they do
2201 // view update batches we don't reenter this code and so that we batch
2202 // all of them together. We don't use
2203 // BeginUpdateViewBatch/EndUpdateViewBatch, since that would reenter this
2204 // exact code, but we want the effect of a single big update batch.
2205 PRBool refreshEnabled = mRefreshEnabled;
2206 mRefreshEnabled = PR_FALSE;
2207 ++mUpdateBatchCnt;
2209 PRInt32 index;
2210 for (index = 0; index < mVMCount; index++) {
2211 nsViewManager* vm = (nsViewManager*)gViewManagers->ElementAt(index);
2212 if (vm->RootViewManager() == this) {
2213 // One of our kids
2214 nsIViewObserver* observer = vm->GetViewObserver();
2215 if (observer) {
2216 observer->WillPaint();
2217 NS_ASSERTION(mUpdateBatchCnt == 1,
2218 "Observer did not end view batch?");
2223 --mUpdateBatchCnt;
2224 // Someone could have called EnableRefresh on us from inside WillPaint().
2225 // Only reset the old mRefreshEnabled value if the current value is false.
2226 if (!mRefreshEnabled) {
2227 mRefreshEnabled = refreshEnabled;
2231 if (mHasPendingUpdates) {
2232 ProcessPendingUpdates(mRootView, PR_TRUE);
2233 mHasPendingUpdates = PR_FALSE;
2237 void
2238 nsViewManager::ProcessInvalidateEvent()
2240 NS_ASSERTION(IsRootVM(),
2241 "Incorrectly targeted invalidate event");
2242 // If we're in the middle of an update batch, just repost the event,
2243 // to be processed when the batch ends.
2244 PRBool processEvent = (mUpdateBatchCnt == 0);
2245 if (processEvent) {
2246 FlushPendingInvalidates();
2248 mInvalidateEvent.Forget();
2249 if (!processEvent) {
2250 // We didn't actually process this event... post a new one
2251 PostInvalidateEvent();
2255 NS_IMETHODIMP
2256 nsViewManager::SetDefaultBackgroundColor(nscolor aColor)
2258 mDefaultBackgroundColor = aColor;
2259 return NS_OK;
2262 NS_IMETHODIMP
2263 nsViewManager::GetDefaultBackgroundColor(nscolor* aColor)
2265 *aColor = mDefaultBackgroundColor;
2266 return NS_OK;
2270 NS_IMETHODIMP
2271 nsViewManager::GetLastUserEventTime(PRUint32& aTime)
2273 aTime = gLastUserEventTime;
2274 return NS_OK;
2277 class nsSynthMouseMoveEvent : public nsViewManagerEvent {
2278 public:
2279 nsSynthMouseMoveEvent(nsViewManager *aViewManager,
2280 PRBool aFromScroll)
2281 : nsViewManagerEvent(aViewManager),
2282 mFromScroll(aFromScroll) {
2285 NS_IMETHOD Run() {
2286 if (mViewManager)
2287 mViewManager->ProcessSynthMouseMoveEvent(mFromScroll);
2288 return NS_OK;
2291 private:
2292 PRBool mFromScroll;
2295 NS_IMETHODIMP
2296 nsViewManager::SynthesizeMouseMove(PRBool aFromScroll)
2298 if (!IsRootVM())
2299 return RootViewManager()->SynthesizeMouseMove(aFromScroll);
2301 if (mMouseLocation == nsPoint(NSCOORD_NONE, NSCOORD_NONE))
2302 return NS_OK;
2304 if (!mSynthMouseMoveEvent.IsPending()) {
2305 nsRefPtr<nsViewManagerEvent> ev =
2306 new nsSynthMouseMoveEvent(this, aFromScroll);
2308 if (NS_FAILED(NS_DispatchToCurrentThread(ev))) {
2309 NS_WARNING("failed to dispatch nsSynthMouseMoveEvent");
2310 return NS_ERROR_UNEXPECTED;
2313 mSynthMouseMoveEvent = ev;
2316 return NS_OK;
2320 * Find the first floating view with a widget in a postorder traversal of the
2321 * view tree that contains the point. Thus more deeply nested floating views
2322 * are preferred over their ancestors, and floating views earlier in the
2323 * view hierarchy (i.e., added later) are preferred over their siblings.
2324 * This is adequate for finding the "topmost" floating view under a point,
2325 * given that floating views don't supporting having a specific z-index.
2327 * We cannot exit early when aPt is outside the view bounds, because floating
2328 * views aren't necessarily included in their parent's bounds, so this could
2329 * traverse the entire view hierarchy --- use carefully.
2331 static nsView* FindFloatingViewContaining(nsView* aView, nsPoint aPt)
2333 for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) {
2334 nsView* r = FindFloatingViewContaining(v, aPt - v->GetOffsetTo(aView));
2335 if (r)
2336 return r;
2339 if (aView->GetFloating() && aView->HasWidget() &&
2340 aView->GetDimensions().Contains(aPt) && IsViewVisible(aView))
2341 return aView;
2343 return nsnull;
2346 void
2347 nsViewManager::ProcessSynthMouseMoveEvent(PRBool aFromScroll)
2349 // allow new event to be posted while handling this one only if the
2350 // source of the event is a scroll (to prevent infinite reflow loops)
2351 if (aFromScroll)
2352 mSynthMouseMoveEvent.Forget();
2354 NS_ASSERTION(IsRootVM(), "Only the root view manager should be here");
2356 if (mMouseLocation == nsPoint(NSCOORD_NONE, NSCOORD_NONE) || !mRootView) {
2357 mSynthMouseMoveEvent.Forget();
2358 return;
2361 // Hold a ref to ourselves so DispatchEvent won't destroy us (since
2362 // we need to access members after we call DispatchEvent).
2363 nsCOMPtr<nsIViewManager> kungFuDeathGrip(this);
2365 #ifdef DEBUG_MOUSE_LOCATION
2366 printf("[vm=%p]synthesizing mouse move to (%d,%d)\n",
2367 this, mMouseLocation.x, mMouseLocation.y);
2368 #endif
2370 nsPoint pt = mMouseLocation;
2371 PRInt32 p2a = mContext->AppUnitsPerDevPixel();
2372 pt.x = NSIntPixelsToAppUnits(mMouseLocation.x, p2a);
2373 pt.y = NSIntPixelsToAppUnits(mMouseLocation.y, p2a);
2374 // This could be a bit slow (traverses entire view hierarchy)
2375 // but it's OK to do it once per synthetic mouse event
2376 nsView* view = FindFloatingViewContaining(mRootView, pt);
2377 nsPoint offset(0, 0);
2378 if (!view) {
2379 view = mRootView;
2380 } else {
2381 offset = view->GetOffsetTo(mRootView);
2382 offset.x = NSAppUnitsToIntPixels(offset.x, p2a);
2383 offset.y = NSAppUnitsToIntPixels(offset.y, p2a);
2385 nsMouseEvent event(PR_TRUE, NS_MOUSE_MOVE, view->GetWidget(),
2386 nsMouseEvent::eSynthesized);
2387 event.refPoint = mMouseLocation - offset;
2388 event.time = PR_IntervalNow();
2389 // XXX set event.isShift, event.isControl, event.isAlt, event.isMeta ?
2391 nsEventStatus status;
2392 view->GetViewManager()->DispatchEvent(&event, &status);
2394 if (!aFromScroll)
2395 mSynthMouseMoveEvent.Forget();
2398 void
2399 nsViewManager::InvalidateHierarchy()
2401 if (mRootView) {
2402 if (!IsRootVM()) {
2403 NS_RELEASE(mRootViewManager);
2405 nsView *parent = mRootView->GetParent();
2406 if (parent) {
2407 mRootViewManager = parent->GetViewManager()->RootViewManager();
2408 NS_ADDREF(mRootViewManager);
2409 NS_ASSERTION(mRootViewManager != this,
2410 "Root view had a parent, but it has the same view manager");
2411 } else {
2412 mRootViewManager = this;