/* -*- 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.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ #include "nsScrollingView.h" #include "nsIWidget.h" #include "nsUnitConversion.h" #include "nsIViewManager.h" #include "nsIPresContext.h" #include "nsIScrollbar.h" #include "nsIDeviceContext.h" #include "nsGUIEvent.h" #include "nsWidgetsCID.h" #include "nsViewsCID.h" #include "nsIScrollableView.h" #include "nsIFrame.h" static NS_DEFINE_IID(kIScrollbarIID, NS_ISCROLLBAR_IID); static NS_DEFINE_IID(kIScrollableViewIID, NS_ISCROLLABLEVIEW_IID); static NS_DEFINE_IID(kWidgetCID, NS_CHILD_CID); class ScrollBarView : public nsView { public: ScrollBarView(nsScrollingView *aScrollingView); ~ScrollBarView(); nsEventStatus HandleEvent(nsGUIEvent *aEvent, PRUint32 aEventFlags); void SetPosition(nscoord x, nscoord y); void SetDimensions(nscoord width, nscoord height, PRBool aPaint = PR_TRUE); public: nsScrollingView *mScrollingView; }; ScrollBarView :: ScrollBarView(nsScrollingView *aScrollingView) { mScrollingView = aScrollingView; } ScrollBarView :: ~ScrollBarView() { } nsEventStatus ScrollBarView :: HandleEvent(nsGUIEvent *aEvent, PRUint32 aEventFlags) { nsEventStatus retval = nsEventStatus_eIgnore; switch (aEvent->message) { case NS_SCROLLBAR_POS: case NS_SCROLLBAR_PAGE_NEXT: case NS_SCROLLBAR_PAGE_PREV: case NS_SCROLLBAR_LINE_NEXT: case NS_SCROLLBAR_LINE_PREV: NS_ASSERTION((nsnull != mScrollingView), "HandleEvent() called after the ScrollingView has been destroyed."); if (nsnull != mScrollingView) mScrollingView->HandleScrollEvent(aEvent, aEventFlags); retval = nsEventStatus_eConsumeNoDefault; break; default: break; } return retval; } void ScrollBarView :: SetPosition(nscoord x, nscoord y) { mBounds.MoveTo(x, y); if (nsnull != mWindow) { nsIPresContext *px = mViewManager->GetPresContext(); float twipToPix = px->GetTwipsToPixels(); nscoord parx = 0, pary = 0; nsIWidget *pwidget = nsnull; pwidget = GetOffsetFromWidget(&parx, &pary); NS_IF_RELEASE(pwidget); mWindow->Move(NSTwipsToIntPixels((x + parx), twipToPix), NSTwipsToIntPixels((y + pary), twipToPix)); NS_RELEASE(px); } } void ScrollBarView :: SetDimensions(nscoord width, nscoord height, PRBool aPaint) { mBounds.SizeTo(width, height); if (nsnull != mWindow) { nsIPresContext *px = mViewManager->GetPresContext(); float t2p = px->GetTwipsToPixels(); mWindow->Resize(NSTwipsToIntPixels(width, t2p), NSTwipsToIntPixels(height, t2p), aPaint); NS_RELEASE(px); } } #if 0 class nsICornerWidget : public nsISupports { public: NS_IMETHOD Init(nsIWidget* aParent, const nsRect& aBounds) = 0; NS_IMETHOD MoveTo(PRInt32 aX, PRInt32 aY) = 0; NS_IMETHOD Show() = 0; NS_IMETHOD Hide() = 0; NS_IMETHOD Start() = 0; NS_IMETHOD Stop() = 0; }; #endif class CornerView : public nsView { public: CornerView(); ~CornerView(); void ShowQuality(PRBool aShow); void SetQuality(nsContentQuality aQuality); void Show(PRBool aShow); PRBool Paint(nsIRenderingContext& rc, const nsRect& rect, PRUint32 aPaintFlags, nsIView *aBackstop = nsnull); PRBool mShowQuality; nsContentQuality mQuality; PRBool mShow; }; CornerView :: CornerView() { mShowQuality = PR_FALSE; mQuality = nsContentQuality_kGood; mShow = PR_FALSE; } CornerView :: ~CornerView() { } void CornerView :: ShowQuality(PRBool aShow) { if (mShowQuality != aShow) { mShowQuality = aShow; if (mShow == PR_FALSE) { if (mVis == nsViewVisibility_kShow) mViewManager->SetViewVisibility(this, nsViewVisibility_kHide); else mViewManager->SetViewVisibility(this, nsViewVisibility_kShow); nscoord dimx, dimy; //this will force the scrolling view to recalc the scrollbar sizes... MMP mParent->GetDimensions(&dimx, &dimy); mParent->SetDimensions(dimx, dimy); } mViewManager->UpdateView(this, nsnull, NS_VMREFRESH_IMMEDIATE); } } void CornerView :: SetQuality(nsContentQuality aQuality) { if (mQuality != aQuality) { mQuality = aQuality; if (mVis == nsViewVisibility_kShow) mViewManager->UpdateView(this, nsnull, NS_VMREFRESH_IMMEDIATE); } } void CornerView :: Show(PRBool aShow) { if (mShow != aShow) { mShow = aShow; if (mShow == PR_TRUE) mViewManager->SetViewVisibility(this, nsViewVisibility_kShow); else if (mShowQuality == PR_FALSE) mViewManager->SetViewVisibility(this, nsViewVisibility_kHide); nscoord dimx, dimy; //this will force the scrolling view to recalc the scrollbar sizes... MMP mParent->GetDimensions(&dimx, &dimy); mParent->SetDimensions(dimx, dimy); } } #ifndef min #define min(a, b) ((a) < (b) ? (a) : (b)) #endif PRBool CornerView :: Paint(nsIRenderingContext& rc, const nsRect& rect, PRUint32 aPaintFlags, nsIView *aBackstop) { PRBool clipres = PR_FALSE; if (mVis == nsViewVisibility_kShow) { nscoord xoff, yoff; nsRect brect; rc.PushState(); GetScrollOffset(&xoff, &yoff); rc.Translate(xoff, yoff); GetBounds(brect); clipres = rc.SetClipRect(brect, nsClipCombine_kIntersect); if (clipres == PR_FALSE) { rc.SetColor(NS_RGB(192, 192, 192)); rc.FillRect(brect); if (PR_TRUE == mShowQuality) { nscolor tcolor, bcolor; //display quality indicator rc.Translate(brect.x, brect.y); rc.SetColor(NS_RGB(0, 0, 0)); rc.FillEllipse(NSToCoordFloor(brect.width * 0.15f), NSToCoordFloor(brect.height * 0.15f), NSToCoordRound(brect.width * 0.7f), // XXX should use NSToCoordCeil ?? NSToCoordRound(brect.height * 0.7f)); // XXX should use NSToCoordCeil ?? if (mQuality == nsContentQuality_kGood) rc.SetColor(NS_RGB(0, 255, 0)); else if (mQuality == nsContentQuality_kFair) rc.SetColor(NS_RGB(255, 176, 0)); else rc.SetColor(NS_RGB(255, 0, 0)); //hey, notice that these numbers don't add up... that's because //something funny happens on windows when the *right* numbers are //used. MMP rc.FillEllipse(NSToCoordRound(brect.width * 0.23f), // XXX should use NSToCoordCeil ?? NSToCoordRound(brect.height * 0.23f), // XXX should use NSToCoordCeil ?? nscoord(brect.width * 0.46f), nscoord(brect.height * 0.46f)); bcolor = tcolor = rc.GetColor(); //this is inefficient, but compact... tcolor = NS_RGB((int)min(NS_GET_R(bcolor) + 40, 255), (int)min(NS_GET_G(bcolor) + 40, 255), (int)min(NS_GET_B(bcolor) + 40, 255)); rc.SetColor(tcolor); rc.FillEllipse(NSToCoordRound(brect.width * 0.34f), // XXX should use NSToCoordCeil ?? NSToCoordRound(brect.height * 0.34f), // XXX should use NSToCoordCeil ?? nscoord(brect.width * 0.28f), nscoord(brect.height * 0.28f)); tcolor = NS_RGB((int)min(NS_GET_R(bcolor) + 120, 255), (int)min(NS_GET_G(bcolor) + 120, 255), (int)min(NS_GET_B(bcolor) + 120, 255)); rc.SetColor(tcolor); rc.FillEllipse(NSToCoordRound(brect.width * 0.32f), // XXX should use NSToCoordCeil ?? NSToCoordRound(brect.height * 0.32f), // XXX should use NSToCoordCeil ?? nscoord(brect.width * 0.17f), nscoord(brect.height * 0.17f)); } } clipres = rc.PopState(); if (clipres == PR_FALSE) { nsRect xrect = brect; xrect.x += xoff; xrect.y += yoff; clipres = rc.SetClipRect(xrect, nsClipCombine_kSubtract); } } return clipres; } static NS_DEFINE_IID(kIViewIID, NS_IVIEW_IID); nsScrollingView :: nsScrollingView() { mSizeX = mSizeY = 0; mOffsetX = mOffsetY = 0; mVScrollBarView = nsnull; mHScrollBarView = nsnull; mCornerView = nsnull; mScrollPref = nsScrollPreference_kAuto; mClipX = mClipY = 0; mScrollingTimer = nsnull; } nsScrollingView :: ~nsScrollingView() { if (nsnull != mVScrollBarView) { // Clear the back-pointer from the scrollbar... ((ScrollBarView*)mVScrollBarView)->mScrollingView = nsnull; } if (nsnull != mHScrollBarView) { // Clear the back-pointer from the scrollbar... ((ScrollBarView*)mHScrollBarView)->mScrollingView = nsnull; } if (nsnull != mCornerView) { mCornerView = nsnull; } } nsresult nsScrollingView :: QueryInterface(const nsIID& aIID, void** aInstancePtr) { if (nsnull == aInstancePtr) { return NS_ERROR_NULL_POINTER; } static NS_DEFINE_IID(kClassIID, NS_ISCROLLABLEVIEW_IID); if (aIID.Equals(kClassIID)) { *aInstancePtr = (void*)(nsIScrollableView*)this; return NS_OK; } return nsView::QueryInterface(aIID, aInstancePtr); } nsrefcnt nsScrollingView :: AddRef() { NS_WARNING("not supported for views"); return 1; } nsrefcnt nsScrollingView :: Release() { NS_WARNING("not supported for views"); return 1; } nsresult nsScrollingView :: Init(nsIViewManager* aManager, const nsRect &aBounds, nsIView *aParent, const nsIID *aWindowIID, nsWidgetInitData *aWidgetInitData, nsNativeWidget aNative, PRInt32 aZIndex, const nsViewClip *aClip, float aOpacity, nsViewVisibility aVisibilityFlag) { nsresult rv; mClipX = aBounds.width; mClipY = aBounds.height; rv = nsView :: Init(aManager, aBounds, aParent, aWindowIID, aWidgetInitData, aNative, aZIndex, aClip, aOpacity, aVisibilityFlag); if (rv == NS_OK) { nsIPresContext *cx = mViewManager->GetPresContext(); nsIDeviceContext *dx = cx->GetDeviceContext(); // Create a view for a corner cover mCornerView = new CornerView(); if (nsnull != mCornerView) { nsRect trect; float sbWidth, sbHeight; dx->GetScrollBarWidth(sbWidth); dx->GetScrollBarHeight(sbHeight); trect.width = NSToCoordRound(sbWidth); trect.x = aBounds.x + aBounds.XMost() - trect.width; trect.height = NSToCoordRound(sbHeight); trect.y = aBounds.y + aBounds.YMost() - trect.height; rv = mCornerView->Init(mViewManager, trect, this, nsnull, nsnull, nsnull, -1, nsnull, 1.0f, nsViewVisibility_kHide); mViewManager->InsertChild(this, mCornerView, -1); } // Create a view for a vertical scrollbar mVScrollBarView = new ScrollBarView(this); if (nsnull != mVScrollBarView) { nsRect trect = aBounds; float sbWidth, sbHeight; dx->GetScrollBarWidth(sbWidth); dx->GetScrollBarHeight(sbHeight); trect.width = NSToCoordRound(sbWidth); trect.x += aBounds.XMost() - trect.width; trect.height -= NSToCoordRound(sbHeight); static NS_DEFINE_IID(kCScrollbarIID, NS_VERTSCROLLBAR_CID); rv = mVScrollBarView->Init(mViewManager, trect, this, &kCScrollbarIID, nsnull, aNative, -3); mViewManager->InsertChild(this, mVScrollBarView, -3); } // Create a view for a horizontal scrollbar mHScrollBarView = new ScrollBarView(this); if (nsnull != mHScrollBarView) { nsRect trect = aBounds; float sbWidth, sbHeight; dx->GetScrollBarWidth(sbWidth); dx->GetScrollBarHeight(sbHeight); trect.height = NSToCoordRound(sbHeight); trect.y += aBounds.YMost() - trect.height; trect.width -= NSToCoordRound(sbWidth); static NS_DEFINE_IID(kCHScrollbarIID, NS_HORZSCROLLBAR_CID); rv = mHScrollBarView->Init(mViewManager, trect, this, &kCHScrollbarIID, nsnull, aNative, -3); mViewManager->InsertChild(this, mHScrollBarView, -3); } NS_RELEASE(dx); NS_RELEASE(cx); } return rv; } void nsScrollingView :: SetDimensions(nscoord width, nscoord height, PRBool aPaint) { nsRect trect; nsIPresContext *cx = mViewManager->GetPresContext(); nsIDeviceContext *dx = cx->GetDeviceContext(); nscoord showHorz = 0, showVert = 0; float scrollWidthFloat, scrollHeightFloat; dx->GetScrollBarWidth(scrollWidthFloat); dx->GetScrollBarHeight(scrollHeightFloat); nscoord scrollWidth = NSToCoordRound(scrollWidthFloat); nscoord scrollHeight = NSToCoordRound(scrollHeightFloat); if (nsnull != mCornerView) { mCornerView->GetDimensions(&trect.width, &trect.height); trect.y = height - scrollHeight; trect.x = width - scrollWidth; mCornerView->SetBounds(trect, aPaint); } if (mHScrollBarView && (mHScrollBarView->GetVisibility() == nsViewVisibility_kShow)) showHorz = scrollHeight; if (mVScrollBarView && (mVScrollBarView->GetVisibility() == nsViewVisibility_kShow)) showVert = scrollWidth; // nsView :: SetDimensions(width, height, aPaint); mBounds.SizeTo(width, height); if (nsnull != mWindow) { float t2p = cx->GetTwipsToPixels(); mClipX = width - showVert; mClipY = height - showHorz; mWindow->Resize(NSTwipsToIntPixels((width - showVert), t2p), NSTwipsToIntPixels((height - showHorz), t2p), aPaint); } else { mClipX = width; mClipY = height; } if (nsnull != mVScrollBarView) { mVScrollBarView->GetDimensions(&trect.width, &trect.height); trect.height = height; if (showHorz || (mCornerView && (mCornerView->GetVisibility() == nsViewVisibility_kShow))) trect.height -= scrollHeight; trect.x = width - scrollWidth; trect.y = 0; mVScrollBarView->SetBounds(trect, aPaint); } if (nsnull != mHScrollBarView) { mHScrollBarView->GetDimensions(&trect.width, &trect.height); trect.width = width; if (showVert || (mCornerView && (mCornerView->GetVisibility() == nsViewVisibility_kShow))) trect.width -= scrollWidth; trect.y = height - scrollHeight; trect.x = 0; mHScrollBarView->SetBounds(trect, aPaint); } //this will fix the size of the thumb when we resize the root window, //but unfortunately it will also cause scrollbar flashing. so long as //all resize operations happen through the viewmanager, this is not //an issue. we'll see. MMP // ComputeContainerSize(); NS_RELEASE(dx); NS_RELEASE(cx); } void nsScrollingView :: SetPosition(nscoord aX, nscoord aY) { nsIPresContext *px = mViewManager->GetPresContext(); nsIWidget *thiswin = GetWidget(); if (nsnull == thiswin) thiswin = GetOffsetFromWidget(nsnull, nsnull); if (nsnull != thiswin) thiswin->BeginResizingChildren(); nsView::SetPosition(aX, aY); AdjustChildWidgets(this, this, 0, 0, px->GetTwipsToPixels()); if (nsnull != thiswin) { thiswin->EndResizingChildren(); NS_RELEASE(thiswin); } NS_RELEASE(px); } PRBool nsScrollingView :: Paint(nsIRenderingContext& rc, const nsRect& rect, PRUint32 aPaintFlags, nsIView *aBackstop) { PRBool clipres = PR_FALSE; nsRect brect; rc.PushState(); GetBounds(brect); if (mVis == nsViewVisibility_kShow) clipres = rc.SetClipRect(brect, nsClipCombine_kIntersect); if (clipres == PR_FALSE) { rc.Translate(-mOffsetX, -mOffsetY); clipres = nsView::Paint(rc, rect, aPaintFlags | NS_VIEW_FLAG_CLIP_SET, aBackstop); } clipres = rc.PopState(); if ((clipres == PR_FALSE) && (mVis == nsViewVisibility_kShow) && (nsnull == mWindow)) clipres = rc.SetClipRect(brect, nsClipCombine_kSubtract); return clipres; } void nsScrollingView :: HandleScrollEvent(nsGUIEvent *aEvent, PRUint32 aEventFlags) { nsIView *scview = nsView::GetViewFor(aEvent->widget); nsIPresContext *px = mViewManager->GetPresContext(); float scale = px->GetTwipsToPixels(); nscoord dx = 0, dy = 0; nsRect bounds; GetBounds(bounds); if ((nsnull != mVScrollBarView) && (scview == mVScrollBarView)) { nscoord oy = mOffsetY; nscoord newpos; //now, this horrible thing makes sure that as we scroll //the document a pixel at a time, we keep the logical position of //our scroll bar at the top edge of the same pixel that //is displayed. newpos = ((nsScrollbarEvent *)aEvent)->position; if ((newpos + bounds.height) > mSizeY) newpos = mSizeY - bounds.height; mOffsetY = NSIntPixelsToTwips(NSTwipsToIntPixels(newpos, scale), px->GetPixelsToTwips()); dy = NSTwipsToIntPixels((oy - mOffsetY), scale); if (dy != 0) { nscoord sx, sy; mVScrollBarView->GetDimensions(&sx, &sy); if ((nsnull != mHScrollBarView) && (mHScrollBarView->GetVisibility() == nsViewVisibility_kShow)) mHScrollBarView->GetDimensions(&sx, &sy); else sy = 0; mViewManager->ClearDirtyRegion(); nsIWidget *thiswin = GetWidget(); if (nsnull == thiswin) thiswin = GetOffsetFromWidget(nsnull, nsnull); if (nsnull != thiswin) thiswin->BeginResizingChildren(); //and now we make sure that the scrollbar thumb is in sync with the //numbers we came up with here, but only if we actually moved at least //a full pixel. if didn't adjust the thumb only if the delta is non-zero, //very slow scrolling would never actually work. ((nsScrollbarEvent *)aEvent)->position = mOffsetY; if (dy != 0) { if (nsnull != mWindow) mWindow->Scroll(0, dy, nsnull); else mViewManager->UpdateView(this, nsnull, 0); } if (nsnull != thiswin) { thiswin->EndResizingChildren(); NS_RELEASE(thiswin); } } } else if ((nsnull != mHScrollBarView) && (scview == mHScrollBarView)) { nscoord ox = mOffsetX; nscoord newpos; //now, this horrible thing makes sure that as we scroll //the document a pixel at a time, we keep the logical position of //our scroll bar at the top edge of the same pixel that //is displayed. newpos = ((nsScrollbarEvent *)aEvent)->position; if ((newpos + bounds.width) > mSizeX) newpos = mSizeX - bounds.width; mOffsetX = NSIntPixelsToTwips(NSTwipsToIntPixels(newpos, scale), px->GetPixelsToTwips()); dx = NSTwipsToIntPixels((ox - mOffsetX), scale); if (dx != 0) { nscoord sx, sy; if ((nsnull != mVScrollBarView) && (mVScrollBarView->GetVisibility() == nsViewVisibility_kShow)) mVScrollBarView->GetDimensions(&sx, &sy); else sx = 0; mHScrollBarView->GetDimensions(&sx, &sy); mViewManager->ClearDirtyRegion(); nsIWidget *thiswin = GetWidget(); if (nsnull == thiswin) thiswin = GetOffsetFromWidget(nsnull, nsnull); if (nsnull != thiswin) thiswin->BeginResizingChildren(); //and now we make sure that the scrollbar thumb is in sync with the //numbers we came up with here, but only if we actually moved at least //a full pixel. if didn't adjust the thumb only if the delta is non-zero, //very slow scrolling would never actually work. ((nsScrollbarEvent *)aEvent)->position = mOffsetX; if (dx != 0) { if (nsnull != mWindow) mWindow->Scroll(dx, 0, nsnull); else mViewManager->UpdateView(this, nsnull, 0); } if (nsnull != thiswin) { thiswin->EndResizingChildren(); NS_RELEASE(thiswin); } } } NS_RELEASE(px); } void nsScrollingView :: Notify(nsITimer * aTimer) { nscoord xoff, yoff; nsIView *view = GetScrolledView(); // First do the scrolling of the view view->GetScrollOffset(&xoff, &yoff); nscoord newPos = yoff + mScrollingDelta; if (newPos < 0) newPos = 0; ScrollTo(0, newPos, 0); // Now fake a mouse event so the frames can process the selection event nsRect rect; nsGUIEvent event; nsEventStatus retval; event.message = NS_MOUSE_MOVE; nsIPresContext *cx = mViewManager->GetPresContext(); GetBounds(rect); event.point.x = rect.x; event.point.y = (mScrollingDelta > 0) ? (rect.height - rect.y - 1) : 135; //printf("timer %d %d\n", event.point.x, event.point.y); mFrame->HandleEvent(*cx, &event, retval); NS_RELEASE(cx); NS_RELEASE(mScrollingTimer); if (NS_OK == NS_NewTimer(&mScrollingTimer)) mScrollingTimer->Init(this, 25); } nsEventStatus nsScrollingView :: HandleEvent(nsGUIEvent *aEvent, PRUint32 aEventFlags) { switch (aEvent->message) { case NS_MOUSE_LEFT_BUTTON_DOWN: case NS_MOUSE_MIDDLE_BUTTON_DOWN: case NS_MOUSE_RIGHT_BUTTON_DOWN: { nsIWidget *win = GetWidget(); if (nsnull != win) { win->SetFocus(); NS_RELEASE(win); } break; } case NS_KEY_DOWN: { nsKeyEvent * keyEvent = (nsKeyEvent *)aEvent; switch (keyEvent->keyCode) { case NS_VK_PAGE_DOWN : case NS_VK_PAGE_UP : { nsIScrollbar *scrollv = nsnull, *scrollh = nsnull; nsIWidget *win = mVScrollBarView->GetWidget(); if (NS_OK == win->QueryInterface(kIScrollbarIID, (void **)&scrollv)) { PRUint32 oldpos = scrollv->GetPosition(); nsRect rect; GetBounds(rect); nscoord newPos = 0; if (keyEvent->keyCode == NS_VK_PAGE_DOWN) { newPos = oldpos+rect.height; } else { newPos = oldpos-rect.height; newPos = (newPos < 0 ? 0 : newPos); } ScrollTo(0, newPos, 0); } } break; case NS_VK_DOWN : case NS_VK_UP : { nsIScrollbar *scrollv = nsnull, *scrollh = nsnull; nsIWidget *win = mVScrollBarView->GetWidget(); if (NS_OK == win->QueryInterface(kIScrollbarIID, (void **)&scrollv)) { PRUint32 oldpos = scrollv->GetPosition(); PRUint32 lineInc = scrollv->GetLineIncrement(); nscoord newPos = 0; if (keyEvent->keyCode == NS_VK_DOWN) { newPos = oldpos+lineInc; } else { newPos = oldpos-lineInc; newPos = (newPos < 0 ? 0 : newPos); } ScrollTo(0, newPos, 0); } } break; default: break; } // switch } break; case NS_MOUSE_MOVE: { nsRect trect; nscoord lx, ly; GetBounds(trect); lx = aEvent->point.x - trect.x; ly = aEvent->point.y - trect.y; //nscoord xoff, yoff; //GetScrolledView()->GetScrollOffset(&xoff, &yoff); //printf("%d %d %d\n", trect.y, trect.height, yoff); //printf("mouse %d %d \n", aEvent->point.x, aEvent->point.y); if (!trect.Contains(lx, ly)) { if (mScrollingTimer == nsnull) { if (nsnull != mFrame) { if (ly < 0 || ly > trect.y) { mScrollingDelta = ly < 0 ? -100 : 100; NS_NewTimer(&mScrollingTimer); mScrollingTimer->Init(this, 25); } } } } else if (mScrollingTimer != nsnull) { mScrollingTimer->Cancel(); NS_RELEASE(mScrollingTimer); } break; } case NS_MOUSE_LEFT_BUTTON_UP: case NS_MOUSE_MIDDLE_BUTTON_UP: case NS_MOUSE_RIGHT_BUTTON_UP: { if (mScrollingTimer != nsnull) { mScrollingTimer->Cancel(); NS_RELEASE(mScrollingTimer); mScrollingTimer = nsnull; } nsRect trect; nscoord lx, ly; GetBounds(trect); lx = aEvent->point.x - trect.x; ly = aEvent->point.y - trect.y; if (!trect.Contains(lx, ly)) { nsEventStatus retval; if (nsnull != mFrame) { nsIPresContext *cx = mViewManager->GetPresContext(); nscoord xoff, yoff; GetScrollOffset(&xoff, &yoff); aEvent->point.x += xoff; aEvent->point.y += yoff; mFrame->HandleEvent(*cx, aEvent, retval); aEvent->point.x -= xoff; aEvent->point.y -= yoff; NS_RELEASE(cx); } } break; } default: break; } return nsView::HandleEvent(aEvent, aEventFlags); } void nsScrollingView :: ComputeContainerSize() { nsIView *scrollview = GetScrolledView(); nsIScrollbar *scrollv = nsnull, *scrollh = nsnull; nsIWidget *win; if (nsnull != scrollview) { nscoord dx = 0, dy = 0; nsIPresContext *px = mViewManager->GetPresContext(); nscoord hwidth, hheight; nscoord vwidth, vheight; PRUint32 oldsizey = mSizeY, oldsizex = mSizeX; nsRect area(0, 0, 0, 0); nscoord offx, offy; float scale = px->GetTwipsToPixels(); ComputeScrollArea(scrollview, area, 0, 0); mSizeY = area.YMost(); mSizeX = area.XMost(); if (nsnull != mHScrollBarView) { mHScrollBarView->GetDimensions(&hwidth, &hheight); win = mHScrollBarView->GetWidget(); if (NS_OK == win->QueryInterface(kIScrollbarIID, (void **)&scrollh)) { if (((mSizeX > mBounds.width) && (mScrollPref != nsScrollPreference_kNeverScroll)) || (mScrollPref == nsScrollPreference_kAlwaysScroll)) scrollh->Release(); //DO NOT USE NS_RELEASE()! MMP else NS_RELEASE(scrollh); //MUST USE NS_RELEASE()! MMP } NS_RELEASE(win); } if (nsnull != mVScrollBarView) { mVScrollBarView->GetDimensions(&vwidth, &vheight); offy = mOffsetY; win = mVScrollBarView->GetWidget(); if (NS_OK == win->QueryInterface(kIScrollbarIID, (void **)&scrollv)) { if ((mSizeY > mBounds.height) && (mScrollPref != nsScrollPreference_kNeverScroll)) { //we need to be able to scroll mVScrollBarView->SetVisibility(nsViewVisibility_kShow); win->Enable(PR_TRUE); //now update the scroller position for the new size PRUint32 oldpos = scrollv->GetPosition(); mOffsetY = NSIntPixelsToTwips(NSTwipsToIntPixels(nscoord(((float)oldpos * mSizeY) / oldsizey), scale), px->GetPixelsToTwips()); dy = NSTwipsToIntPixels((offy - mOffsetY), scale); scrollv->SetParameters(mSizeY, mBounds.height - ((nsnull != scrollh) ? hheight : 0), mOffsetY, NSIntPointsToTwips(12)); } else { mOffsetY = 0; dy = NSTwipsToIntPixels(offy, scale); if (mScrollPref == nsScrollPreference_kAlwaysScroll) { mVScrollBarView->SetVisibility(nsViewVisibility_kShow); win->Enable(PR_FALSE); } else { mVScrollBarView->SetVisibility(nsViewVisibility_kHide); win->Enable(PR_TRUE); NS_RELEASE(scrollv); } } //don't release the vertical scroller here because if we need to //create a horizontal one, it will need to know that there is a vertical one // //create a horizontal one, it will need to tweak the vertical one } NS_RELEASE(win); } if (nsnull != mHScrollBarView) { offx = mOffsetX; win = mHScrollBarView->GetWidget(); if (NS_OK == win->QueryInterface(kIScrollbarIID, (void **)&scrollh)) { if ((mSizeX > mBounds.width) && (mScrollPref != nsScrollPreference_kNeverScroll)) { //we need to be able to scroll mHScrollBarView->SetVisibility(nsViewVisibility_kShow); win->Enable(PR_TRUE); //now update the scroller position for the new size PRUint32 oldpos = scrollh->GetPosition(); mOffsetX = NSIntPixelsToTwips(NSTwipsToIntPixels(nscoord(((float)oldpos * mSizeX) / oldsizex), scale), px->GetPixelsToTwips()); dx = NSTwipsToIntPixels((offx - mOffsetX), scale); scrollh->SetParameters(mSizeX, mBounds.width - ((nsnull != scrollv) ? vwidth : 0), mOffsetX, NSIntPointsToTwips(12)); // //now make the vertical scroll region account for this scrollbar // // if (nsnull != scrollv) // scrollv->SetParameters(mSizeY, mBounds.height - hheight, mOffsetY, NSIntPointsToTwips(12)); } else { mOffsetX = 0; dx = NSTwipsToIntPixels(offx, scale); if (mScrollPref == nsScrollPreference_kAlwaysScroll) { mHScrollBarView->SetVisibility(nsViewVisibility_kShow); win->Enable(PR_FALSE); } else { mHScrollBarView->SetVisibility(nsViewVisibility_kHide); win->Enable(PR_TRUE); } } NS_RELEASE(scrollh); } NS_RELEASE(win); } if (mCornerView) { if ((mHScrollBarView && (mHScrollBarView->GetVisibility() == nsViewVisibility_kShow)) && (mVScrollBarView && (mVScrollBarView->GetVisibility() == nsViewVisibility_kShow))) ((CornerView *)mCornerView)->Show(PR_TRUE); else ((CornerView *)mCornerView)->Show(PR_FALSE); } // now we can release the vertical scroller if there was one... NS_IF_RELEASE(scrollv); // if ((dx != 0) || (dy != 0)) // AdjustChildWidgets(this, this, 0, 0, px->GetTwipsToPixels()); NS_RELEASE(px); } else { if (nsnull != mHScrollBarView) { mHScrollBarView->SetVisibility(nsViewVisibility_kHide); win = mHScrollBarView->GetWidget(); if (NS_OK == win->QueryInterface(kIScrollbarIID, (void **)&scrollh)) { scrollh->SetParameters(0, 0, 0, 0); NS_RELEASE(scrollh); } NS_RELEASE(win); } if (nsnull != mVScrollBarView) { mVScrollBarView->SetVisibility(nsViewVisibility_kHide); win = mVScrollBarView->GetWidget(); if (NS_OK == win->QueryInterface(kIScrollbarIID, (void **)&scrollv)) { scrollv->SetParameters(0, 0, 0, 0); NS_RELEASE(scrollv); } NS_RELEASE(win); } if (nsnull != mCornerView) ((CornerView *)mCornerView)->Show(PR_FALSE); mOffsetX = mOffsetY = 0; mSizeX = mSizeY = 0; } } void nsScrollingView :: GetContainerSize(nscoord *aWidth, nscoord *aHeight) { *aWidth = mSizeX; *aHeight = mSizeY; } void nsScrollingView :: SetVisibleOffset(nscoord aOffsetX, nscoord aOffsetY) { mOffsetX = aOffsetX; mOffsetY = aOffsetY; } void nsScrollingView :: GetVisibleOffset(nscoord *aOffsetX, nscoord *aOffsetY) { *aOffsetX = mOffsetX; *aOffsetY = mOffsetY; } void nsScrollingView :: ShowQuality(PRBool aShow) { ((CornerView *)mCornerView)->ShowQuality(aShow); } PRBool nsScrollingView :: GetShowQuality(void) { return ((CornerView *)mCornerView)->mShowQuality; } void nsScrollingView :: SetQuality(nsContentQuality aQuality) { ((CornerView *)mCornerView)->SetQuality(aQuality); } void nsScrollingView :: SetScrollPreference(nsScrollPreference aPref) { mScrollPref = aPref; ComputeContainerSize(); } nsScrollPreference nsScrollingView :: GetScrollPreference(void) { return mScrollPref; } // XXX This doesn't do X scrolling yet // XXX This doesn't let the scrolling code slide the bits on the // screen and damage only the appropriate area // XXX doesn't smooth scroll NS_IMETHODIMP nsScrollingView :: ScrollTo(nscoord aX, nscoord aY, PRUint32 aUpdateFlags) { nsIPresContext *px = mViewManager->GetPresContext(); float t2p = px->GetTwipsToPixels(); float p2t = px->GetPixelsToTwips(); NS_RELEASE(px); nsIWidget* win; win = mVScrollBarView->GetWidget(); if (nsnull != win) { nsIScrollbar* scrollv; if (NS_OK == win->QueryInterface(kIScrollbarIID, (void **)&scrollv)) { // Clamp aY nsRect r; GetBounds(r); if (aY + r.height > mSizeY) { aY = mSizeY - r.height; if (aY < 0) { aY = 0; } } // Move the scrollbar's thumb PRUint32 oldpos = mOffsetY; nscoord dy; PRUint32 newpos = NSIntPixelsToTwips(NSTwipsToIntPixels(aY, t2p), p2t); scrollv->SetPosition(newpos); dy = oldpos - newpos; // Update offsets SetVisibleOffset(aX, aY); AdjustChildWidgets(this, this, 0, 0, t2p); // Damage the updated area r.x = 0; r.y = aY; nsIView* scrolledView = GetScrolledView(); if (nsnull != scrolledView) { mViewManager->UpdateView(scrolledView, r, aUpdateFlags); } NS_RELEASE(scrollv); } NS_RELEASE(win); } return NS_OK; } NS_IMETHODIMP nsScrollingView :: GetClipSize(nscoord *aX, nscoord *aY) { *aX = mClipX; *aY = mClipY; return NS_OK; } void nsScrollingView :: AdjustChildWidgets(nsScrollingView *aScrolling, nsIView *aView, nscoord aDx, nscoord aDy, float scale) { PRInt32 numkids = aView->GetChildCount(); nsIScrollableView *scroller; nscoord offx, offy; PRBool isscroll = PR_FALSE; if (aScrolling == aView) { nsIWidget *widget = aScrolling->GetOffsetFromWidget(&aDx, &aDy); nsIView *parview = aScrolling->GetParent(); while (nsnull != parview) { nsIWidget *parwidget = parview->GetWidget(); if (NS_OK == parview->QueryInterface(kIScrollableViewIID, (void **)&scroller)) { scroller->GetVisibleOffset(&offx, &offy); aDx -= offx; aDy -= offy; } if (parwidget == widget) { NS_IF_RELEASE(parwidget); break; } NS_IF_RELEASE(parwidget); parview = parview->GetParent(); } NS_IF_RELEASE(widget); } aView->GetPosition(&offx, &offy); aDx += offx; aDy += offy; if (NS_OK == aView->QueryInterface(kIScrollableViewIID, (void **)&scroller)) { scroller->GetVisibleOffset(&offx, &offy); aDx -= offx; aDy -= offy; isscroll = PR_TRUE; } for (PRInt32 cnt = 0; cnt < numkids; cnt++) { nsIView *kid = aView->GetChild(cnt); nsIWidget *win = kid->GetWidget(); if (nsnull != win) { nsRect bounds; win->BeginResizingChildren(); kid->GetBounds(bounds); if (!isscroll || (isscroll && (kid != ((nsScrollingView *)aView)->mVScrollBarView) && (kid != ((nsScrollingView *)aView)->mHScrollBarView))) win->Move(NSTwipsToIntPixels((bounds.x + aDx), scale), NSTwipsToIntPixels((bounds.y + aDy), scale)); else win->Move(NSTwipsToIntPixels((bounds.x + aDx + offx), scale), NSTwipsToIntPixels((bounds.y + aDy + offy), scale)); } AdjustChildWidgets(aScrolling, kid, aDx, aDy, scale); if (nsnull != win) { win->EndResizingChildren(); NS_RELEASE(win); } } } nsIView * nsScrollingView :: GetScrolledView(void) { PRInt32 numkids; nsIView *retview = nsnull; numkids = GetChildCount(); for (PRInt32 cnt = 0; cnt < numkids; cnt++) { retview = GetChild(cnt); if ((retview != mVScrollBarView) && (retview != mHScrollBarView) && (retview != mCornerView)) break; else retview = nsnull; } return retview; } void nsScrollingView :: ComputeScrollArea(nsIView *aView, nsRect &aRect, nscoord aOffX, nscoord aOffY) { nsRect trect, vrect; aView->GetBounds(vrect); aOffX += vrect.x; aOffY += vrect.y; trect.x = aOffX; trect.y = aOffY; trect.width = vrect.width; trect.height = vrect.height; if (aRect.IsEmpty() == PR_TRUE) aRect = trect; else aRect.UnionRect(aRect, trect); PRInt32 numkids = aView->GetChildCount(); for (PRInt32 cnt = 0; cnt < numkids; cnt++) { nsIView *view = aView->GetChild(cnt); ComputeScrollArea(view, aRect, aOffX, aOffY); } }