Mozilla/mozilla/layout/html/base/src/nsScrollPortFrame.cpp
troy%netscape.com e90db6dfb9 Changes to bring clipping inline with upcoming CSS2 errata
git-svn-id: svn://10.0.0.236/trunk@66887 18797224-902f-48f8-a5cc-f745e15eee43
2000-04-23 00:56:16 +00:00

474 lines
13 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Pierre Phaneuf <pp@ludusdesign.com>
*/
#include "nsCOMPtr.h"
#include "nsHTMLParts.h"
#include "nsIPresContext.h"
#include "nsIStyleContext.h"
#include "nsIReflowCommand.h"
#include "nsIDeviceContext.h"
#include "nsPageFrame.h"
#include "nsViewsCID.h"
#include "nsIView.h"
#include "nsIViewManager.h"
#include "nsHTMLContainerFrame.h"
#include "nsHTMLIIDs.h"
#include "nsCSSRendering.h"
#include "nsIScrollableView.h"
#include "nsWidgetsCID.h"
#include "nsScrollPortFrame.h"
#include "nsLayoutAtoms.h"
#include "nsIBox.h"
#include "nsBoxLayoutState.h"
#include "nsIBoxToBlockAdaptor.h"
/*
static NS_DEFINE_IID(kWidgetCID, NS_CHILD_CID);
static NS_DEFINE_IID(kScrollingViewCID, NS_SCROLLING_VIEW_CID);
static NS_DEFINE_IID(kViewCID, NS_VIEW_CID);
static NS_DEFINE_IID(kIViewIID, NS_IVIEW_IID);
static NS_DEFINE_IID(kScrollViewIID, NS_ISCROLLABLEVIEW_IID);
static NS_DEFINE_IID(kAreaFrameIID, NS_IAREAFRAME_IID);
*/
static NS_DEFINE_IID(kWidgetCID, NS_CHILD_CID);
static NS_DEFINE_IID(kScrollPortViewCID, NS_SCROLL_PORT_VIEW_CID);
static NS_DEFINE_IID(kViewCID, NS_VIEW_CID);
static NS_DEFINE_IID(kIViewIID, NS_IVIEW_IID);
static NS_DEFINE_IID(kScrollViewIID, NS_ISCROLLABLEVIEW_IID);
//----------------------------------------------------------------------
nsresult
NS_NewScrollPortFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame)
{
NS_PRECONDITION(aNewFrame, "null OUT ptr");
if (nsnull == aNewFrame) {
return NS_ERROR_NULL_POINTER;
}
nsScrollPortFrame* it = new (aPresShell) nsScrollPortFrame (aPresShell);
if (nsnull == it) {
return NS_ERROR_OUT_OF_MEMORY;
}
*aNewFrame = it;
return NS_OK;
}
nsScrollPortFrame::nsScrollPortFrame(nsIPresShell* aShell):nsBoxFrame(aShell)
{
}
NS_IMETHODIMP
nsScrollPortFrame::Init(nsIPresContext* aPresContext,
nsIContent* aContent,
nsIFrame* aParent,
nsIStyleContext* aStyleContext,
nsIFrame* aPrevInFlow)
{
nsresult rv = nsBoxFrame::Init(aPresContext, aContent,
aParent, aStyleContext,
aPrevInFlow);
// Create the scrolling view
CreateScrollingView(aPresContext);
return rv;
}
NS_IMETHODIMP
nsScrollPortFrame::SetInitialChildList(nsIPresContext* aPresContext,
nsIAtom* aListName,
nsIFrame* aChildList)
{
nsresult rv = nsBoxFrame::SetInitialChildList(aPresContext, aListName,
aChildList);
nsIFrame* frame = mFrames.FirstChild();
// There must be one and only one child frame
if (!frame) {
return NS_ERROR_INVALID_ARG;
} else if (mFrames.GetLength() > 1) {
return NS_ERROR_UNEXPECTED;
}
#ifdef NS_DEBUG
// Verify that the scrolled frame has a view
nsIView* scrolledView;
frame->GetView(aPresContext, &scrolledView);
NS_ASSERTION(nsnull != scrolledView, "no view");
#endif
// We need to allow the view's position to be different than the
// frame's position
nsFrameState state;
frame->GetFrameState(&state);
state &= ~NS_FRAME_SYNC_FRAME_AND_VIEW;
frame->SetFrameState(state);
return rv;
}
NS_IMETHODIMP
nsScrollPortFrame::AppendFrames(nsIPresContext* aPresContext,
nsIPresShell& aPresShell,
nsIAtom* aListName,
nsIFrame* aFrameList)
{
// Only one child frame allowed
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsScrollPortFrame::InsertFrames(nsIPresContext* aPresContext,
nsIPresShell& aPresShell,
nsIAtom* aListName,
nsIFrame* aPrevFrame,
nsIFrame* aFrameList)
{
// Only one child frame allowed
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsScrollPortFrame::RemoveFrame(nsIPresContext* aPresContext,
nsIPresShell& aPresShell,
nsIAtom* aListName,
nsIFrame* aOldFrame)
{
// Scroll frame doesn't support incremental changes
return NS_ERROR_NOT_IMPLEMENTED;
}
nsresult
nsScrollPortFrame::CreateScrollingViewWidget(nsIView* aView, const nsStylePosition* aPosition)
{
nsresult rv = NS_OK;
// If it's fixed positioned, then create a widget
if (NS_STYLE_POSITION_FIXED == aPosition->mPosition) {
rv = aView->CreateWidget(kWidgetCID);
}
return(rv);
}
nsresult
nsScrollPortFrame::GetScrollingParentView(nsIPresContext* aPresContext,
nsIFrame* aParent,
nsIView** aParentView)
{
nsresult rv = aParent->GetView(aPresContext, aParentView);
NS_ASSERTION(aParentView, "GetParentWithView failed");
return(rv);
}
nsresult
nsScrollPortFrame::CreateScrollingView(nsIPresContext* aPresContext)
{
nsIView* view;
//Get parent frame
nsIFrame* parent;
GetParentWithView(aPresContext, &parent);
NS_ASSERTION(parent, "GetParentWithView failed");
// Get parent view
nsIView* parentView = nsnull;
GetScrollingParentView(aPresContext, parent, &parentView);
// Get the view manager
nsIViewManager* viewManager;
parentView->GetViewManager(viewManager);
// Create the scrolling view
nsresult rv = nsComponentManager::CreateInstance(kScrollPortViewCID,
nsnull,
kIViewIID,
(void **)&view);
if (NS_OK == rv) {
const nsStylePosition* position = (const nsStylePosition*)
mStyleContext->GetStyleData(eStyleStruct_Position);
const nsStyleColor* color = (const nsStyleColor*)
mStyleContext->GetStyleData(eStyleStruct_Color);
const nsStyleSpacing* spacing = (const nsStyleSpacing*)
mStyleContext->GetStyleData(eStyleStruct_Spacing);
const nsStyleDisplay* display = (const nsStyleDisplay*)
mStyleContext->GetStyleData(eStyleStruct_Display);
// Get the z-index
PRInt32 zIndex = 0;
if (eStyleUnit_Integer == position->mZIndex.GetUnit()) {
zIndex = position->mZIndex.GetIntValue();
}
// Initialize the scrolling view
view->Init(viewManager, mRect, parentView, display->IsVisibleOrCollapsed() ?
nsViewVisibility_kShow : nsViewVisibility_kHide);
// Insert the view into the view hierarchy
viewManager->InsertChild(parentView, view, zIndex);
// Set the view's opacity
viewManager->SetViewOpacity(view, color->mOpacity);
// Because we only paint the border and we don't paint a background,
// inform the view manager that we have transparent content
viewManager->SetViewContentTransparency(view, PR_TRUE);
// If it's fixed positioned, then create a widget too
CreateScrollingViewWidget(view, position);
// Get the nsIScrollableView interface
nsIScrollableView* scrollingView;
view->QueryInterface(kScrollViewIID, (void**)&scrollingView);
scrollingView->SetScrollPreference(nsScrollPreference_kNeverScroll);
// Have the scrolling view create its internal widgets
scrollingView->CreateScrollControls();
// Set the scrolling view's insets to whatever our border is
nsMargin border;
if (!spacing->GetBorder(border)) {
NS_NOTYETIMPLEMENTED("percentage border");
border.SizeTo(0, 0, 0, 0);
}
scrollingView->SetControlInsets(border);
// Remember our view
SetView(aPresContext, view);
}
NS_RELEASE(viewManager);
return rv;
}
NS_IMETHODIMP
nsScrollPortFrame::GetMargin(nsMargin& aMargin)
{
aMargin.SizeTo(0,0,0,0);
return NS_OK;
}
NS_IMETHODIMP
nsScrollPortFrame::GetPadding(nsMargin& aMargin)
{
aMargin.SizeTo(0,0,0,0);
return NS_OK;
}
NS_IMETHODIMP
nsScrollPortFrame::GetBorder(nsMargin& aMargin)
{
aMargin.SizeTo(0,0,0,0);
return NS_OK;
}
NS_IMETHODIMP
nsScrollPortFrame::Layout(nsBoxLayoutState& aState)
{
nsRect clientRect(0,0,0,0);
GetClientRect(clientRect);
nsIBox* kid = nsnull;
GetChildBox(&kid);
nsRect childRect(clientRect);
nsMargin margin(0,0,0,0);
kid->GetMargin(margin);
childRect.Deflate(margin);
nsSize min(0,0);
kid->GetMinSize(aState, min);
/*
// if our child is not html then get is min size
// and make sure we don't squeeze it smaller than that.
nsIBoxToBlockAdaptor* adaptor = nsnull;
if (NS_FAILED(kid->QueryInterface(NS_GET_IID(nsIBoxToBlockAdaptor), (void**)&adaptor))) {
if (min.height > childRect.height)
childRect.height = min.height;
}
*/
if (min.height > childRect.height)
childRect.height = min.height;
if (min.width > childRect.width)
childRect.width = min.width;
kid->SetBounds(aState, childRect);
kid->Layout(aState);
kid->GetBounds(childRect);
clientRect.Inflate(margin);
if (childRect.width < clientRect.width || childRect.height < childRect.height)
{
if (childRect.width < clientRect.width)
childRect.width = clientRect.width;
if (childRect.height < clientRect.height)
childRect.height = clientRect.height;
clientRect.Deflate(margin);
kid->SetBounds(aState, childRect);
}
SyncLayout(aState);
nsIPresContext* presContext = aState.GetPresContext();
nsIScrollableView* scrollingView;
nsIView* view;
GetView(presContext, &view);
if (NS_SUCCEEDED(view->QueryInterface(kScrollViewIID, (void**)&scrollingView))) {
scrollingView->ComputeScrollOffsets(PR_TRUE);
}
return NS_OK;
}
NS_IMETHODIMP
nsScrollPortFrame::GetPrefSize(nsBoxLayoutState& aBoxLayoutState, nsSize& aSize)
{
nsIBox* child = nsnull;
GetChildBox(&child);
nsresult rv = child->GetPrefSize(aBoxLayoutState, aSize);
AddMargin(child, aSize);
AddBorderAndPadding(aSize);
AddInset(aSize);
nsIBox::AddCSSPrefSize(aBoxLayoutState, this, aSize);
return rv;
}
NS_IMETHODIMP
nsScrollPortFrame::GetMinSize(nsBoxLayoutState& aBoxLayoutState, nsSize& aSize)
{
aSize.width = 0;
aSize.height = 0;
AddBorderAndPadding(aSize);
AddInset(aSize);
nsIBox::AddCSSMinSize(aBoxLayoutState, this, aSize);
return NS_OK;
}
NS_IMETHODIMP
nsScrollPortFrame::GetMaxSize(nsBoxLayoutState& aBoxLayoutState, nsSize& aSize)
{
nsIBox* child = nsnull;
GetChildBox(&child);
nsresult rv = child->GetMaxSize(aBoxLayoutState, aSize);
AddMargin(child, aSize);
AddBorderAndPadding(aSize);
AddInset(aSize);
nsIBox::AddCSSMaxSize(aBoxLayoutState, this, aSize);
return rv;
}
NS_IMETHODIMP
nsScrollPortFrame::Paint(nsIPresContext* aPresContext,
nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect,
nsFramePaintLayer aWhichLayer)
{
if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) {
// Only paint the border and background if we're visible
const nsStyleDisplay* display = (const nsStyleDisplay*)
mStyleContext->GetStyleData(eStyleStruct_Display);
if (display->IsVisibleOrCollapsed()) {
// Paint our border only (no background)
const nsStyleSpacing* spacing = (const nsStyleSpacing*)
mStyleContext->GetStyleData(eStyleStruct_Spacing);
nsRect rect(0, 0, mRect.width, mRect.height);
nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, this,
aDirtyRect, rect, *spacing, mStyleContext, 0);
}
}
// Paint our children
nsresult rv = nsBoxFrame::Paint(aPresContext, aRenderingContext, aDirtyRect,
aWhichLayer);
return rv;
}
PRIntn
nsScrollPortFrame::GetSkipSides() const
{
return 0;
}
nsresult
nsScrollPortFrame::GetContentOf(nsIContent** aContent)
{
return GetContent(aContent);
}
NS_IMETHODIMP
nsScrollPortFrame::GetFrameType(nsIAtom** aType) const
{
NS_PRECONDITION(nsnull != aType, "null OUT parameter pointer");
*aType = nsLayoutAtoms::scrollFrame;
NS_ADDREF(*aType);
return NS_OK;
}
#ifdef NS_DEBUG
NS_IMETHODIMP
nsScrollPortFrame::GetFrameName(nsString& aResult) const
{
return MakeFrameName("ScrollPort", aResult);
}
#endif
NS_IMETHODIMP_(nsrefcnt)
nsScrollPortFrame::AddRef(void)
{
return NS_OK;
}
NS_IMETHODIMP_(nsrefcnt)
nsScrollPortFrame::Release(void)
{
return NS_OK;
}
NS_INTERFACE_MAP_BEGIN(nsScrollPortFrame)
NS_INTERFACE_MAP_ENTRY(nsIBox)
#ifdef NS_DEBUG
NS_INTERFACE_MAP_ENTRY(nsIFrameDebug)
#endif
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIBox)
NS_INTERFACE_MAP_END_INHERITING(nsBoxFrame)