1856 lines
53 KiB
C++
1856 lines
53 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* 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 Communicator client code.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Netscape Communications Corporation.
|
|
* Portions created by the Initial Developer are Copyright (C) 1998
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Original Author: David W. Hyatt (hyatt@netscape.com)
|
|
* Mike Pinkerton (pinkerton@netscape.com)
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the NPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the NPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
#include "nsCOMPtr.h"
|
|
#include "nsXULTreeOuterGroupFrame.h"
|
|
#include "nsXULAtoms.h"
|
|
#include "nsHTMLAtoms.h"
|
|
#include "nsIContent.h"
|
|
#include "nsINameSpaceManager.h"
|
|
#include "nsIScrollableFrame.h"
|
|
#include "nsIScrollbarFrame.h"
|
|
#include "nsISupportsArray.h"
|
|
#include "nsCSSFrameConstructor.h"
|
|
#include "nsIDocument.h"
|
|
#include "nsTreeItemDragCapturer.h"
|
|
#include "nsIDOMEventReceiver.h"
|
|
#include "nsIDOMMouseEvent.h"
|
|
#include "nsIDragService.h"
|
|
#include "nsIServiceManager.h"
|
|
#include "nsIScrollableView.h"
|
|
#include "nsTreeLayout.h"
|
|
#include "nsITimer.h"
|
|
#include "nsIBindingManager.h"
|
|
#include "nsScrollPortFrame.h"
|
|
#include "nsIRenderingContext.h"
|
|
#include "nsIDeviceContext.h"
|
|
#include "nsIFontMetrics.h"
|
|
#include "nsIStyleContext.h"
|
|
#include "nsIDOMText.h"
|
|
|
|
#include "nsGridRowGroupLayout.h"
|
|
|
|
#define TICK_FACTOR 50
|
|
|
|
// the longest amount of time that can go by before the use
|
|
// notices it as a delay.
|
|
#define USER_TIME_THRESHOLD 150000
|
|
|
|
// how long it takes to layout a single row inital value.
|
|
// we will time this after we scroll a few rows.
|
|
#define TIME_PER_ROW_INITAL 50000
|
|
|
|
// if we decide we can't layout the rows in the amount of time. How long
|
|
// do we wait before checking again?
|
|
#define SMOOTH_INTERVAL 100
|
|
|
|
|
|
nsresult NS_NewAutoScrollTimer(nsXULTreeOuterGroupFrame* aTree, nsDragAutoScrollTimer **aResult) ;
|
|
|
|
//
|
|
// nsDragOverListener
|
|
//
|
|
// Just a little class that listens for dragOvers to trigger the auto-scrolling
|
|
// code.
|
|
//
|
|
class nsDragOverListener : public nsIDOMDragListener
|
|
{
|
|
public:
|
|
|
|
nsDragOverListener ( nsXULTreeOuterGroupFrame* inTree )
|
|
: mTree ( inTree )
|
|
{ NS_INIT_REFCNT(); }
|
|
|
|
virtual ~nsDragOverListener() { } ;
|
|
|
|
NS_DECL_ISUPPORTS
|
|
|
|
// nsIDOMDragListener
|
|
NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent) { return NS_OK; }
|
|
NS_IMETHOD DragEnter(nsIDOMEvent* aDragEvent) { return NS_OK; }
|
|
NS_IMETHOD DragOver(nsIDOMEvent* aDragEvent);
|
|
NS_IMETHOD DragExit(nsIDOMEvent* aDragEvent) { return NS_OK; }
|
|
NS_IMETHOD DragDrop(nsIDOMEvent* aDragEvent) { return NS_OK; }
|
|
NS_IMETHOD DragGesture(nsIDOMEvent* aDragEvent) { return NS_OK; }
|
|
|
|
protected:
|
|
|
|
nsXULTreeOuterGroupFrame* mTree;
|
|
|
|
}; // nsDragEnterListener
|
|
|
|
|
|
NS_IMPL_ISUPPORTS2(nsDragOverListener, nsIDOMEventListener, nsIDOMDragListener)
|
|
|
|
|
|
//
|
|
// DragOver
|
|
//
|
|
// Kick off our timer/capturing for autoscrolling, the drag has entered us. We
|
|
// will continue capturing the mouse until the auto-scroll manager tells us to
|
|
// stop (either the drag is over, it left the window, or someone else wants a
|
|
// crack at auto-scrolling).
|
|
//
|
|
nsresult
|
|
nsDragOverListener :: DragOver(nsIDOMEvent* aDragEvent)
|
|
{
|
|
nsCOMPtr<nsIDOMMouseEvent> mouseEvent ( do_QueryInterface(aDragEvent) );
|
|
if ( mouseEvent ) {
|
|
PRInt32 x = 0, y = 0;
|
|
mouseEvent->GetClientX ( &x );
|
|
mouseEvent->GetClientY ( &y );
|
|
mTree->HandleAutoScrollTracking ( nsPoint(x,y) );
|
|
}
|
|
return NS_OK;
|
|
|
|
} // DragOver
|
|
|
|
|
|
|
|
#ifdef XP_MAC
|
|
#pragma mark -
|
|
#endif
|
|
|
|
/* A mediator used to smooth out scrolling. It works by seeing if
|
|
* we have time to scroll the amount of rows requested. This is determined
|
|
* by measuring how long it takes to scroll a row. If we can scroll the
|
|
* rows in time we do so. If not we start a timer and skip the request. We
|
|
* do this until the timer finally first because the user has stopped moving
|
|
* the mouse. Then do all the queued requests in on shot.
|
|
*/
|
|
class nsScrollSmoother : public nsITimerCallback
|
|
{
|
|
public:
|
|
|
|
NS_IMETHOD_(void) Notify(nsITimer *timer);
|
|
|
|
void Start();
|
|
void Stop();
|
|
PRBool IsRunning();
|
|
|
|
NS_DECL_ISUPPORTS
|
|
virtual ~nsScrollSmoother();
|
|
|
|
nsScrollSmoother(nsXULTreeOuterGroupFrame* aOuter);
|
|
|
|
nsCOMPtr<nsITimer> mRepeatTimer;
|
|
PRBool mDelta;
|
|
nsXULTreeOuterGroupFrame* mOuter;
|
|
};
|
|
|
|
nsScrollSmoother::nsScrollSmoother(nsXULTreeOuterGroupFrame* aOuter)
|
|
{
|
|
NS_INIT_REFCNT();
|
|
mDelta = 0;
|
|
mOuter = aOuter;
|
|
}
|
|
|
|
nsScrollSmoother::~nsScrollSmoother()
|
|
{
|
|
Stop();
|
|
}
|
|
|
|
|
|
PRBool nsScrollSmoother::IsRunning()
|
|
{
|
|
if (mRepeatTimer)
|
|
return PR_TRUE;
|
|
else
|
|
return PR_FALSE;
|
|
}
|
|
|
|
void nsScrollSmoother::Start()
|
|
{
|
|
Stop();
|
|
mRepeatTimer = do_CreateInstance("@mozilla.org/timer;1");
|
|
mRepeatTimer->Init(this, SMOOTH_INTERVAL);
|
|
}
|
|
|
|
void nsScrollSmoother::Stop()
|
|
{
|
|
if ( mRepeatTimer ) {
|
|
mRepeatTimer->Cancel();
|
|
mRepeatTimer = nsnull;
|
|
}
|
|
}
|
|
|
|
NS_IMETHODIMP_(void) nsScrollSmoother::Notify(nsITimer *timer)
|
|
{
|
|
//printf("Timer Callback!\n");
|
|
|
|
Stop();
|
|
|
|
NS_ASSERTION(mOuter, "mOuter is null, see bug #68365");
|
|
if (!mOuter) return;
|
|
|
|
// actually do some work.
|
|
mOuter->InternalPositionChangedCallback();
|
|
}
|
|
|
|
NS_IMPL_ISUPPORTS1(nsScrollSmoother, nsITimerCallback)
|
|
|
|
|
|
//
|
|
// NS_NewXULTreeOuterGroupFrame
|
|
//
|
|
// Creates a new TreeOuterGroup frame
|
|
//
|
|
nsresult
|
|
NS_NewXULTreeOuterGroupFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame, PRBool aIsRoot,
|
|
nsIBoxLayout* aLayoutManager)
|
|
{
|
|
NS_PRECONDITION(aNewFrame, "null OUT ptr");
|
|
if (nsnull == aNewFrame) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
nsXULTreeOuterGroupFrame* it = new (aPresShell) nsXULTreeOuterGroupFrame(aPresShell, aIsRoot, aLayoutManager);
|
|
if (!it)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
*aNewFrame = it;
|
|
return NS_OK;
|
|
|
|
} // NS_NewXULTreeOuterGroupFrame
|
|
|
|
|
|
// Constructor
|
|
nsXULTreeOuterGroupFrame::nsXULTreeOuterGroupFrame(nsIPresShell* aPresShell, PRBool aIsRoot, nsIBoxLayout* aLayoutManager)
|
|
: nsXULTreeGroupFrame(aPresShell, aIsRoot, aLayoutManager),
|
|
mBatchCount(0),
|
|
mRowGroupInfo(nsnull),
|
|
mRowHeight(0),
|
|
mCurrentIndex(0),
|
|
mOldIndex(0),
|
|
mTreeIsSorted(PR_FALSE),
|
|
mCanDropBetweenRows(PR_TRUE),
|
|
mDragOverListener(nsnull),
|
|
mRowHeightWasSet(PR_FALSE),
|
|
mReflowCallbackPosted(PR_FALSE),
|
|
mScrolling(PR_FALSE),
|
|
mAdjustScroll(PR_FALSE),
|
|
mYPosition(0),
|
|
mScrollSmoother(nsnull),
|
|
mTimePerRow(TIME_PER_ROW_INITAL),
|
|
mTreeItemTag(nsXULAtoms::treeitem),
|
|
mTreeRowTag(nsXULAtoms::treerow),
|
|
mTreeChildrenTag(nsXULAtoms::treechildren),
|
|
mStringWidth(-1)
|
|
{
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsXULTreeOuterGroupFrame::Destroy(nsIPresContext* aPresContext)
|
|
{
|
|
|
|
// make sure we cancel any posted callbacks.
|
|
if (mReflowCallbackPosted) {
|
|
nsCOMPtr<nsIPresShell> shell;
|
|
aPresContext->GetShell(getter_AddRefs(shell));
|
|
shell->CancelReflowCallback(this);
|
|
}
|
|
|
|
|
|
return nsXULTreeGroupFrame::Destroy(aPresContext);
|
|
}
|
|
|
|
// Destructor
|
|
nsXULTreeOuterGroupFrame::~nsXULTreeOuterGroupFrame()
|
|
{
|
|
NS_IF_RELEASE(mScrollSmoother);
|
|
|
|
// TODO cancel posted events.
|
|
|
|
nsCOMPtr<nsIContent> content;
|
|
GetContent(getter_AddRefs(content));
|
|
nsCOMPtr<nsIDOMEventReceiver> receiver(do_QueryInterface(content));
|
|
|
|
// NOTE: the last Remove will delete the drag capturer
|
|
if ( receiver && mDragOverListener )
|
|
receiver->RemoveEventListener(NS_LITERAL_STRING("dragover"), mDragOverListener, PR_TRUE);
|
|
|
|
delete mRowGroupInfo;
|
|
|
|
#if USE_TIMER_TO_DELAY_SCROLLING
|
|
StopScrollTracking();
|
|
mAutoScrollTimer = nsnull;
|
|
#endif
|
|
|
|
}
|
|
|
|
NS_IMETHODIMP_(nsrefcnt)
|
|
nsXULTreeOuterGroupFrame::AddRef(void)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP_(nsrefcnt)
|
|
nsXULTreeOuterGroupFrame::Release(void)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
//
|
|
// QueryInterface
|
|
//
|
|
NS_INTERFACE_MAP_BEGIN(nsXULTreeOuterGroupFrame)
|
|
NS_INTERFACE_MAP_ENTRY(nsIScrollbarMediator)
|
|
NS_INTERFACE_MAP_ENTRY(nsIReflowCallback)
|
|
NS_INTERFACE_MAP_END_INHERITING(nsXULTreeGroupFrame)
|
|
|
|
|
|
//
|
|
// Init
|
|
//
|
|
// Setup scrolling and event listeners for drag auto-scrolling
|
|
//
|
|
NS_IMETHODIMP
|
|
nsXULTreeOuterGroupFrame::Init(nsIPresContext* aPresContext, nsIContent* aContent,
|
|
nsIFrame* aParent, nsIStyleContext* aContext, nsIFrame* aPrevInFlow)
|
|
{
|
|
nsresult rv = nsXULTreeGroupFrame::Init(aPresContext, aContent, aParent, aContext, aPrevInFlow);
|
|
|
|
nsAutoString value;
|
|
mContent->GetAttr(kNameSpaceID_None, nsXULAtoms::treeitem, value);
|
|
if (!value.IsEmpty())
|
|
mTreeItemTag = getter_AddRefs(NS_NewAtom(value));
|
|
mContent->GetAttr(kNameSpaceID_None, nsXULAtoms::treerow, value);
|
|
if (!value.IsEmpty())
|
|
mTreeRowTag = getter_AddRefs(NS_NewAtom(value));
|
|
mContent->GetAttr(kNameSpaceID_None, nsXULAtoms::treechildren, value);
|
|
if (!value.IsEmpty())
|
|
mTreeChildrenTag = getter_AddRefs(NS_NewAtom(value));
|
|
|
|
// mLayingOut = PR_FALSE;
|
|
|
|
float p2t;
|
|
aPresContext->GetScaledPixelsToTwips(&p2t);
|
|
mOnePixel = NSIntPixelsToTwips(1, p2t);
|
|
|
|
nsIFrame* box;
|
|
aParent->GetParent(&box);
|
|
if (!box)
|
|
return rv;
|
|
|
|
nsCOMPtr<nsIScrollableFrame> scrollFrame(do_QueryInterface(box));
|
|
if (!scrollFrame)
|
|
return rv;
|
|
|
|
nsIScrollableView* scrollableView;
|
|
scrollFrame->GetScrollableView(aPresContext, &scrollableView);
|
|
scrollableView->SetScrollProperties(NS_SCROLL_PROPERTY_ALWAYS_BLIT);
|
|
|
|
nsIBox* verticalScrollbar;
|
|
scrollFrame->GetScrollbarBox(PR_TRUE, &verticalScrollbar);
|
|
if (!verticalScrollbar) {
|
|
NS_ERROR("Unable to install the scrollbar mediator on the tree widget. You must be using GFX scrollbars.");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
nsCOMPtr<nsIScrollbarFrame> scrollbarFrame(do_QueryInterface(verticalScrollbar));
|
|
scrollbarFrame->SetScrollbarMediator(this);
|
|
|
|
nsBoxLayoutState boxLayoutState(aPresContext);
|
|
|
|
const nsStyleFont* font = (const nsStyleFont*)aContext->GetStyleData(eStyleStruct_Font);
|
|
nsCOMPtr<nsIDeviceContext> dc;
|
|
aPresContext->GetDeviceContext(getter_AddRefs(dc));
|
|
nsCOMPtr<nsIFontMetrics> fm;
|
|
dc->GetMetricsFor(font->mFont, *getter_AddRefs(fm));
|
|
fm->GetHeight(mRowHeight);
|
|
|
|
// Our frame's lifetime is bounded by the lifetime of the content model, so we're guaranteed
|
|
// that the content node won't go away on us. As a result, our listener can't go away before the
|
|
// frame is deleted. Since the content node holds owning references to our drag capturer, which
|
|
// we tear down in the dtor, there is no need to hold an owning ref to it ourselves.
|
|
nsCOMPtr<nsIDOMEventReceiver> receiver(do_QueryInterface(aContent));
|
|
if ( receiver ) {
|
|
mDragOverListener = new nsDragOverListener(this);
|
|
receiver->AddEventListener(NS_LITERAL_STRING("dragover"), mDragOverListener, PR_FALSE);
|
|
}
|
|
|
|
// our parent is the <tree> tag. check if it has an attribute denying the ability to
|
|
// drop between rows and cache it here for the benefit of the rows inside us.
|
|
nsCOMPtr<nsIContent> parent;
|
|
GetTreeContent(getter_AddRefs(parent));
|
|
|
|
if ( parent ) {
|
|
nsAutoString attr;
|
|
parent->GetAttr ( kNameSpaceID_None, nsXULAtoms::ddNoDropBetweenRows, attr );
|
|
if ( attr.Equals(NS_LITERAL_STRING("true")) )
|
|
mCanDropBetweenRows = PR_FALSE;
|
|
}
|
|
|
|
return rv;
|
|
|
|
} // Init
|
|
|
|
NS_IMETHODIMP
|
|
nsXULTreeOuterGroupFrame::NeedsRecalc()
|
|
{
|
|
mStringWidth = -1;
|
|
return nsXULTreeGroupFrame::NeedsRecalc();
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsXULTreeOuterGroupFrame::GetPrefSize(nsBoxLayoutState& aBoxLayoutState, nsSize& aSize)
|
|
{
|
|
// NeedsRecalc(); // Don't think this is needed any more.
|
|
return nsXULTreeGroupFrame::GetPrefSize(aBoxLayoutState, aSize);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsXULTreeOuterGroupFrame::DoLayout(nsBoxLayoutState& aBoxLayoutState)
|
|
{
|
|
if (mScrolling)
|
|
aBoxLayoutState.SetDisablePainting(PR_TRUE);
|
|
|
|
nsresult rv = nsXULTreeGroupFrame::DoLayout(aBoxLayoutState);
|
|
|
|
if (mScrolling)
|
|
aBoxLayoutState.SetDisablePainting(PR_FALSE);
|
|
|
|
// if we are scrolled and the row height changed
|
|
// make sure we are scrolled to a correct index.
|
|
if (mAdjustScroll)
|
|
PostReflowCallback();
|
|
|
|
return rv;
|
|
}
|
|
|
|
PRInt32
|
|
nsXULTreeOuterGroupFrame::GetFixedRowSize()
|
|
{
|
|
PRInt32 dummy;
|
|
|
|
nsCOMPtr<nsIContent> parent;
|
|
GetTreeContent(getter_AddRefs(parent));
|
|
nsAutoString rows;
|
|
parent->GetAttr(kNameSpaceID_None, nsXULAtoms::rows, rows);
|
|
if (!rows.IsEmpty())
|
|
return rows.ToInteger(&dummy);
|
|
|
|
parent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::size, rows);
|
|
|
|
if (!rows.IsEmpty())
|
|
return rows.ToInteger(&dummy);
|
|
|
|
return -1;
|
|
}
|
|
|
|
void
|
|
nsXULTreeOuterGroupFrame::SetRowHeight(nscoord aRowHeight)
|
|
{
|
|
if (aRowHeight > mRowHeight) {
|
|
mRowHeight = aRowHeight;
|
|
|
|
nsCOMPtr<nsIContent> parent;
|
|
GetTreeContent(getter_AddRefs(parent));
|
|
nsAutoString rows;
|
|
parent->GetAttr(kNameSpaceID_None, nsXULAtoms::rows, rows);
|
|
if (rows.IsEmpty())
|
|
parent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::size, rows);
|
|
|
|
if (!rows.IsEmpty()) {
|
|
PRInt32 dummy;
|
|
PRInt32 count = rows.ToInteger(&dummy);
|
|
float t2p;
|
|
mPresContext->GetTwipsToPixels(&t2p);
|
|
PRInt32 rowHeight = NSTwipsToIntPixels(aRowHeight, t2p);
|
|
nsAutoString value;
|
|
value.AppendInt(rowHeight*count);
|
|
mContent->SetAttr(kNameSpaceID_None, nsXULAtoms::minheight, value, PR_FALSE);
|
|
}
|
|
|
|
// signal we need to dirty everything
|
|
// and we want to be notified after reflow
|
|
// so we can create or destory rows as needed
|
|
mRowHeightWasSet = PR_TRUE;
|
|
PostReflowCallback();
|
|
}
|
|
}
|
|
|
|
nscoord
|
|
nsXULTreeOuterGroupFrame::GetYPosition()
|
|
{
|
|
return mYPosition;
|
|
}
|
|
|
|
void
|
|
nsXULTreeOuterGroupFrame::VerticalScroll(PRInt32 aPosition)
|
|
{
|
|
nsIBox* box;
|
|
GetParentBox(&box);
|
|
if (!box)
|
|
return;
|
|
|
|
box->GetParentBox(&box);
|
|
if (!box)
|
|
return;
|
|
|
|
nsCOMPtr<nsIScrollableFrame> scrollFrame(do_QueryInterface(box));
|
|
if (!scrollFrame)
|
|
return;
|
|
|
|
nscoord x, y;
|
|
scrollFrame->GetScrollPosition(mPresContext, x, y);
|
|
|
|
scrollFrame->ScrollTo(mPresContext, x, aPosition, NS_SCROLL_PROPERTY_ALWAYS_BLIT);
|
|
|
|
mYPosition = aPosition;
|
|
}
|
|
|
|
nscoord
|
|
nsXULTreeOuterGroupFrame::GetAvailableHeight()
|
|
{
|
|
nsIBox* box;
|
|
GetParentBox(&box);
|
|
if (!box)
|
|
return 0;
|
|
|
|
nsRect contentRect;
|
|
box->GetContentRect(contentRect);
|
|
return contentRect.height;
|
|
}
|
|
|
|
void
|
|
nsXULTreeOuterGroupFrame::ComputeTotalRowCount(PRInt32& aCount, nsIContent* aParent)
|
|
{
|
|
if (!mRowGroupInfo) {
|
|
mRowGroupInfo = new nsXULTreeRowGroupInfo();
|
|
}
|
|
|
|
nsCOMPtr<nsIContent> parent = aParent;
|
|
if (aParent == mContent) {
|
|
nsCOMPtr<nsIContent> content;
|
|
mContent->GetBindingParent(getter_AddRefs(content));
|
|
if (content)
|
|
GetTreeContent(getter_AddRefs(parent));
|
|
}
|
|
|
|
PRInt32 childCount;
|
|
parent->ChildCount(childCount);
|
|
|
|
for (PRInt32 i = 0; i < childCount; i++) {
|
|
nsCOMPtr<nsIContent> childContent;
|
|
parent->ChildAt(i, *getter_AddRefs(childContent));
|
|
nsCOMPtr<nsIAtom> tag;
|
|
childContent->GetTag(*getter_AddRefs(tag));
|
|
if (tag == mTreeRowTag) {
|
|
if ((aCount%TICK_FACTOR) == 0)
|
|
mRowGroupInfo->Add(childContent);
|
|
|
|
mRowGroupInfo->mLastChild = childContent;
|
|
|
|
aCount++;
|
|
}
|
|
else if (tag == mTreeItemTag) {
|
|
// Descend into this row group and try to find the next row.
|
|
ComputeTotalRowCount(aCount, childContent);
|
|
}
|
|
else if (tag == mTreeChildrenTag) {
|
|
// If it's open, descend into its treechildren.
|
|
nsCOMPtr<nsIAtom> openAtom = dont_AddRef(NS_NewAtom("open"));
|
|
nsAutoString isOpen;
|
|
nsCOMPtr<nsIContent> parent;
|
|
childContent->GetParent(*getter_AddRefs(parent));
|
|
parent->GetAttr(kNameSpaceID_None, openAtom, isOpen);
|
|
if (isOpen.Equals(NS_LITERAL_STRING("true")))
|
|
ComputeTotalRowCount(aCount, childContent);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
nsXULTreeOuterGroupFrame::PostReflowCallback()
|
|
{
|
|
if (!mReflowCallbackPosted) {
|
|
mReflowCallbackPosted = PR_TRUE;
|
|
nsCOMPtr<nsIPresShell> shell;
|
|
mPresContext->GetShell(getter_AddRefs(shell));
|
|
shell->PostReflowCallback(this);
|
|
}
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsXULTreeOuterGroupFrame::VisibilityChanged(PRBool aVisible)
|
|
{
|
|
if (!aVisible && mCurrentIndex > 0)
|
|
EnsureRowIsVisible(0);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsXULTreeOuterGroupFrame::ScrollbarButtonPressed(PRInt32 aOldIndex, PRInt32 aNewIndex)
|
|
{
|
|
if (aOldIndex == aNewIndex)
|
|
return NS_OK;
|
|
if (aNewIndex < aOldIndex)
|
|
mCurrentIndex--;
|
|
else mCurrentIndex++;
|
|
if (mCurrentIndex < 0) {
|
|
mCurrentIndex = 0;
|
|
return NS_OK;
|
|
}
|
|
InternalPositionChanged(aNewIndex < aOldIndex, 1);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsXULTreeOuterGroupFrame::PositionChanged(PRInt32 aOldIndex, PRInt32& aNewIndex)
|
|
{
|
|
if (mScrolling)
|
|
return NS_OK;
|
|
|
|
PRInt32 oldTwipIndex, newTwipIndex;
|
|
oldTwipIndex = mCurrentIndex*mRowHeight;
|
|
newTwipIndex = (aNewIndex*mOnePixel);
|
|
PRInt32 twipDelta = newTwipIndex > oldTwipIndex ? newTwipIndex - oldTwipIndex : oldTwipIndex - newTwipIndex;
|
|
|
|
PRInt32 rowDelta = twipDelta / mRowHeight;
|
|
PRInt32 remainder = twipDelta % mRowHeight;
|
|
if (remainder > (mRowHeight/2))
|
|
rowDelta++;
|
|
|
|
if (rowDelta == 0)
|
|
return NS_OK;
|
|
|
|
// update the position to be row based.
|
|
|
|
PRInt32 newIndex = newTwipIndex > oldTwipIndex ? mCurrentIndex + rowDelta : mCurrentIndex - rowDelta;
|
|
//aNewIndex = newIndex*mRowHeight/mOnePixel;
|
|
|
|
nsScrollSmoother* smoother = GetSmoother();
|
|
|
|
//printf("%d rows, %d per row, Estimated time needed %d, Threshhold %d (%s)\n", rowDelta, mTimePerRow, mTimePerRow * rowDelta, USER_TIME_THRESHOLD, (mTimePerRow * rowDelta > USER_TIME_THRESHOLD) ? "Nope" : "Yep");
|
|
|
|
// if we can't scroll the rows in time then start a timer. We will eat
|
|
// events until the user stops moving and the timer stops.
|
|
if (smoother->IsRunning() || rowDelta*mTimePerRow > USER_TIME_THRESHOLD) {
|
|
|
|
smoother->Stop();
|
|
|
|
nsCOMPtr<nsIPresShell> shell;
|
|
mPresContext->GetShell(getter_AddRefs(shell));
|
|
shell->FlushPendingNotifications(PR_FALSE);
|
|
|
|
smoother->mDelta = newTwipIndex > oldTwipIndex ? rowDelta : -rowDelta;
|
|
|
|
//printf("Eating scroll!\n");
|
|
|
|
smoother->Start();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
smoother->Stop();
|
|
|
|
mCurrentIndex = newIndex;
|
|
smoother->mDelta = 0;
|
|
|
|
if (mCurrentIndex < 0) {
|
|
mCurrentIndex = 0;
|
|
return NS_OK;
|
|
}
|
|
|
|
return InternalPositionChanged(newTwipIndex < oldTwipIndex, rowDelta);
|
|
}
|
|
|
|
nsScrollSmoother*
|
|
nsXULTreeOuterGroupFrame::GetSmoother()
|
|
{
|
|
if (!mScrollSmoother) {
|
|
mScrollSmoother = new nsScrollSmoother(this);
|
|
NS_ASSERTION(mScrollSmoother, "out of memory");
|
|
NS_IF_ADDREF(mScrollSmoother);
|
|
}
|
|
|
|
return mScrollSmoother;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsXULTreeOuterGroupFrame::InternalPositionChangedCallback()
|
|
{
|
|
nsScrollSmoother* smoother = GetSmoother();
|
|
|
|
if (smoother->mDelta == 0)
|
|
return NS_OK;
|
|
|
|
mCurrentIndex += smoother->mDelta;
|
|
|
|
if (mCurrentIndex < 0)
|
|
mCurrentIndex = 0;
|
|
|
|
return InternalPositionChanged(smoother->mDelta < 0, smoother->mDelta < 0 ? -smoother->mDelta : smoother->mDelta);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsXULTreeOuterGroupFrame::InternalPositionChanged(PRBool aUp, PRInt32 aDelta, PRBool aForceDestruct)
|
|
{
|
|
if (aDelta == 0)
|
|
return NS_OK;
|
|
|
|
// begin timing how long it takes to scroll a row
|
|
PRTime start = PR_Now();
|
|
|
|
//printf("Actually doing scroll mCurrentIndex=%d, delta=%d!\n", mCurrentIndex, aDelta);
|
|
|
|
nsCOMPtr<nsIPresShell> shell;
|
|
mPresContext->GetShell(getter_AddRefs(shell));
|
|
shell->FlushPendingNotifications(PR_FALSE);
|
|
|
|
PRInt32 visibleRows = 0;
|
|
if (mRowHeight)
|
|
visibleRows = GetAvailableHeight()/mRowHeight;
|
|
|
|
// Get our presentation context.
|
|
if (aDelta < visibleRows && !aForceDestruct) {
|
|
PRInt32 loseRows = aDelta;
|
|
|
|
// scrolling down
|
|
if (!aUp) {
|
|
// Figure out how many rows we have to lose off the top.
|
|
DestroyRows(loseRows);
|
|
}
|
|
// scrolling up
|
|
else {
|
|
// Get our first row content.
|
|
nsCOMPtr<nsIContent> rowContent;
|
|
GetFirstRowContent(getter_AddRefs(rowContent));
|
|
|
|
// Figure out how many rows we have to lose off the bottom.
|
|
ReverseDestroyRows(loseRows);
|
|
|
|
// Now that we've lost some rows, we need to create a
|
|
// content chain that provides a hint for moving forward.
|
|
nsCOMPtr<nsIContent> topRowContent;
|
|
PRInt32 findContent = aDelta;
|
|
FindPreviousRowContent(findContent, rowContent, nsnull, getter_AddRefs(topRowContent));
|
|
ConstructContentChain(topRowContent);
|
|
//Now construct the chain for the old top row so its content chain gets
|
|
//set up correctly.
|
|
ConstructOldContentChain(rowContent);
|
|
}
|
|
}
|
|
else {
|
|
// Just blow away all our frames, but keep a content chain
|
|
// as a hint to figure out how to build the frames.
|
|
// Remove the scrollbar first.
|
|
// get the starting row index and row count
|
|
nsIBox* currBox;
|
|
GetChildBox(&currBox);
|
|
while (currBox) {
|
|
nsIBox* nextBox;
|
|
currBox->GetNextBox(&nextBox);
|
|
nsIFrame* frame;
|
|
currBox->QueryInterface(NS_GET_IID(nsIFrame), (void**)&frame);
|
|
mFrameConstructor->RemoveMappingsForFrameSubtree(mPresContext, frame, nsnull);
|
|
currBox = nextBox;
|
|
}
|
|
|
|
nsBoxLayoutState state(mPresContext);
|
|
ClearChildren(state);
|
|
mFrames.DestroyFrames(mPresContext);
|
|
|
|
nsCOMPtr<nsIContent> topRowContent;
|
|
FindRowContentAtIndex(mCurrentIndex, mContent, getter_AddRefs(topRowContent));
|
|
|
|
if (topRowContent)
|
|
ConstructContentChain(topRowContent);
|
|
}
|
|
|
|
mTopFrame = mBottomFrame = nsnull; // Make sure everything is cleared out.
|
|
|
|
mYPosition = mCurrentIndex*mRowHeight;
|
|
nsBoxLayoutState state(mPresContext);
|
|
mScrolling = PR_TRUE;
|
|
MarkDirtyChildren(state);
|
|
shell->FlushPendingNotifications(PR_FALSE);
|
|
mScrolling = PR_FALSE;
|
|
|
|
VerticalScroll(mYPosition);
|
|
|
|
if (aForceDestruct)
|
|
Redraw(state, nsnull, PR_FALSE);
|
|
|
|
PRTime end = PR_Now();
|
|
|
|
PRTime difTime;
|
|
LL_SUB(difTime, end, start);
|
|
|
|
PRInt32 newTime;
|
|
LL_L2I(newTime, difTime);
|
|
newTime /= aDelta;
|
|
|
|
// average old and new
|
|
mTimePerRow = (newTime + mTimePerRow)/2;
|
|
|
|
//printf("time per row=%d\n", mTimePerRow);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
void
|
|
nsXULTreeOuterGroupFrame::ConstructContentChain(nsIContent* aRowContent)
|
|
{
|
|
// Create the content chain array.
|
|
NS_IF_RELEASE(mContentChain);
|
|
NS_NewISupportsArray(&mContentChain);
|
|
|
|
nsCOMPtr<nsIContent> treeContent;
|
|
GetTreeContent(getter_AddRefs(treeContent));
|
|
|
|
// Move up the chain until we hit our content node.
|
|
nsCOMPtr<nsIContent> currContent = dont_QueryInterface(aRowContent);
|
|
while (currContent && (currContent.get() != mContent) &&
|
|
currContent != treeContent) {
|
|
mContentChain->InsertElementAt(currContent, 0);
|
|
nsCOMPtr<nsIContent> otherContent = currContent;
|
|
otherContent->GetParent(*getter_AddRefs(currContent));
|
|
}
|
|
|
|
NS_ASSERTION(currContent.get() == mContent || currContent == treeContent,
|
|
"Disaster! Content not contained in our tree!\n");
|
|
}
|
|
|
|
void
|
|
nsXULTreeOuterGroupFrame::ConstructOldContentChain(nsIContent* aOldRowContent)
|
|
{
|
|
nsCOMPtr<nsIContent> childOfCommonAncestor;
|
|
|
|
//Find the first child of the common ancestor between the new top row's content chain
|
|
//and the old top row. Everything between this child and the old top row potentially need
|
|
//to have their content chains reset.
|
|
FindChildOfCommonContentChainAncestor(aOldRowContent, getter_AddRefs(childOfCommonAncestor));
|
|
|
|
if (childOfCommonAncestor) {
|
|
//Set up the old top rows content chian.
|
|
CreateOldContentChain(aOldRowContent, childOfCommonAncestor);
|
|
}
|
|
}
|
|
|
|
void
|
|
nsXULTreeOuterGroupFrame::FindChildOfCommonContentChainAncestor(nsIContent *startContent, nsIContent **child)
|
|
{
|
|
PRUint32 count;
|
|
if (mContentChain)
|
|
{
|
|
nsresult rv = mContentChain->Count(&count);
|
|
|
|
if (NS_SUCCEEDED(rv) && (count >0)) {
|
|
for (PRInt32 curItem = count - 1; curItem >= 0; curItem--) {
|
|
nsCOMPtr<nsISupports> supports;
|
|
mContentChain->GetElementAt(curItem, getter_AddRefs(supports));
|
|
nsCOMPtr<nsIContent> curContent = do_QueryInterface(supports);
|
|
|
|
//See if curContent is an ancestor of startContent.
|
|
if (IsAncestor(curContent, startContent, child))
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
//mContent isn't actually put in the content chain, so we need to
|
|
//check it separately.
|
|
if (IsAncestor(mContent, startContent, child))
|
|
return;
|
|
|
|
*child = nsnull;
|
|
}
|
|
|
|
|
|
// if oldRowContent is an ancestor of rowContent, return true,
|
|
// and return the previous ancestor if requested
|
|
PRBool
|
|
nsXULTreeOuterGroupFrame::IsAncestor(nsIContent *aRowContent, nsIContent *aOldRowContent, nsIContent** firstDescendant)
|
|
{
|
|
nsCOMPtr<nsIContent> prevContent;
|
|
nsCOMPtr<nsIContent> currContent = dont_QueryInterface(aOldRowContent);
|
|
|
|
while (currContent) {
|
|
if (aRowContent == currContent.get()) {
|
|
if (firstDescendant) {
|
|
*firstDescendant = prevContent;
|
|
NS_IF_ADDREF(*firstDescendant);
|
|
}
|
|
return PR_TRUE;
|
|
}
|
|
|
|
prevContent = currContent;
|
|
prevContent->GetParent(*getter_AddRefs(currContent));
|
|
}
|
|
|
|
return PR_FALSE;
|
|
}
|
|
|
|
void
|
|
nsXULTreeOuterGroupFrame::CreateOldContentChain(nsIContent* aOldRowContent, nsIContent* topOfChain)
|
|
{
|
|
nsCOMPtr<nsIContent> currContent = dont_QueryInterface(aOldRowContent);
|
|
nsCOMPtr<nsIContent> prevContent;
|
|
|
|
nsCOMPtr<nsIPresShell> shell;
|
|
mPresContext->GetShell(getter_AddRefs(shell));
|
|
|
|
//For each item between the (oldtoprow) and
|
|
// (the new first child of common ancestry between new top row and old top row)
|
|
// we need to see if the content chain has to be reset.
|
|
while (currContent.get() != topOfChain) {
|
|
nsIFrame* primaryFrame = nsnull;
|
|
shell->GetPrimaryFrameFor(currContent, &primaryFrame);
|
|
|
|
if (primaryFrame) {
|
|
nsCOMPtr<nsIXULTreeSlice> slice(do_QueryInterface(primaryFrame));
|
|
PRBool isRowGroup = PR_FALSE;
|
|
if (slice)
|
|
slice->IsGroupFrame(&isRowGroup);
|
|
|
|
if (isRowGroup) {
|
|
//Get the current content's parent's first child
|
|
nsCOMPtr<nsIContent> parent;
|
|
currContent->GetParent(*getter_AddRefs(parent));
|
|
|
|
nsCOMPtr<nsIContent> firstChild;
|
|
parent->ChildAt(0, *getter_AddRefs(firstChild));
|
|
|
|
nsIFrame* parentFrame;
|
|
primaryFrame->GetParent(&parentFrame);
|
|
PRBool isParentRowGroup = PR_FALSE;
|
|
nsCOMPtr<nsIXULTreeSlice> slice(do_QueryInterface(parentFrame));
|
|
if (slice)
|
|
slice->IsGroupFrame(&isParentRowGroup);
|
|
|
|
if (isParentRowGroup) {
|
|
//Get the current content's parent's first frame.
|
|
nsXULTreeGroupFrame *parentRowGroupFrame =
|
|
(nsXULTreeGroupFrame*)parentFrame;
|
|
nsIFrame *currentTopFrame = parentRowGroupFrame->GetFirstFrame();
|
|
|
|
nsCOMPtr<nsIContent> topContent;
|
|
currentTopFrame->GetContent(getter_AddRefs(topContent));
|
|
|
|
// If the current content's parent's first child is different
|
|
// than the current frame's parent's first child then we know
|
|
// they are out of synch and we need to set the content
|
|
// chain correctly.
|
|
if(topContent.get() != firstChild.get()) {
|
|
nsCOMPtr<nsISupportsArray> contentChain;
|
|
NS_NewISupportsArray(getter_AddRefs(contentChain));
|
|
contentChain->InsertElementAt(firstChild, 0);
|
|
parentRowGroupFrame->SetContentChain(contentChain);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
prevContent = currContent;
|
|
prevContent->GetParent(*getter_AddRefs(currContent));
|
|
}
|
|
}
|
|
|
|
void
|
|
nsXULTreeOuterGroupFrame::FindRowContentAtIndex(PRInt32& aIndex,
|
|
nsIContent* aParent,
|
|
nsIContent** aResult)
|
|
{
|
|
// Init to nsnull.
|
|
*aResult = nsnull;
|
|
|
|
// Walk over the tick array.
|
|
if (mRowGroupInfo == nsnull)
|
|
return;
|
|
|
|
PRUint32 index = 0;
|
|
PRUint32 arrayCount;
|
|
mRowGroupInfo->mTickArray->Count(&arrayCount);
|
|
nsCOMPtr<nsIContent> startContent;
|
|
PRUint32 location = aIndex/TICK_FACTOR + 1;
|
|
PRUint32 point = location*TICK_FACTOR;
|
|
if (location >= arrayCount) {
|
|
startContent = mRowGroupInfo->mLastChild;
|
|
point = mRowGroupInfo->mRowCount-1;
|
|
}
|
|
else {
|
|
nsCOMPtr<nsISupports> supp = getter_AddRefs(mRowGroupInfo->mTickArray->ElementAt(location));
|
|
startContent = do_QueryInterface(supp);
|
|
}
|
|
|
|
if (!startContent) {
|
|
NS_ERROR("The tree's tick array is confused!");
|
|
return;
|
|
}
|
|
|
|
PRInt32 delta = (PRInt32)(point-aIndex);
|
|
if (delta == 0) {
|
|
*aResult = startContent;
|
|
NS_IF_ADDREF(*aResult);
|
|
}
|
|
else FindPreviousRowContent(delta, startContent, nsnull, aResult);
|
|
}
|
|
|
|
void
|
|
nsXULTreeOuterGroupFrame::FindPreviousRowContent(PRInt32& aDelta, nsIContent* aUpwardHint,
|
|
nsIContent* aDownwardHint,
|
|
nsIContent** aResult)
|
|
{
|
|
// Init to nsnull.
|
|
*aResult = nsnull;
|
|
|
|
// It disappoints me that this function is completely tied to the content nodes,
|
|
// but I can't see any other way to handle this. I don't have the frames, so I have nothing
|
|
// else to fall back on but the content nodes.
|
|
PRInt32 index = 0;
|
|
nsCOMPtr<nsIContent> parentContent;
|
|
if (aUpwardHint) {
|
|
aUpwardHint->GetParent(*getter_AddRefs(parentContent));
|
|
NS_ASSERTION(parentContent, "Parent content null in the upward hint of FPRC\n");
|
|
if (!parentContent)
|
|
return;
|
|
parentContent->IndexOf(aUpwardHint, index);
|
|
}
|
|
else if (aDownwardHint) {
|
|
parentContent = dont_QueryInterface(aDownwardHint);
|
|
parentContent->ChildCount(index);
|
|
}
|
|
|
|
for (PRInt32 i = index-1; i >= 0; i--) {
|
|
nsCOMPtr<nsIContent> childContent;
|
|
parentContent->ChildAt(i, *getter_AddRefs(childContent));
|
|
nsCOMPtr<nsIAtom> tag;
|
|
childContent->GetTag(*getter_AddRefs(tag));
|
|
if (tag == mTreeRowTag) {
|
|
aDelta--;
|
|
if (aDelta == 0) {
|
|
*aResult = childContent;
|
|
NS_IF_ADDREF(*aResult);
|
|
return;
|
|
}
|
|
}
|
|
else if (tag == mTreeItemTag) {
|
|
// If it's open, descend into its treechildren node first.
|
|
nsCOMPtr<nsIAtom> openAtom = dont_AddRef(NS_NewAtom("open"));
|
|
nsAutoString isOpen;
|
|
childContent->GetAttr(kNameSpaceID_None, openAtom, isOpen);
|
|
if (isOpen.Equals(NS_LITERAL_STRING("true"))) {
|
|
// Find the <treechildren> node.
|
|
PRInt32 childContentCount;
|
|
nsCOMPtr<nsIContent> grandChild;
|
|
childContent->ChildCount(childContentCount);
|
|
|
|
PRInt32 j;
|
|
for (j = childContentCount-1; j >= 0; j--) {
|
|
|
|
childContent->ChildAt(j, *getter_AddRefs(grandChild));
|
|
nsCOMPtr<nsIAtom> grandChildTag;
|
|
grandChild->GetTag(*getter_AddRefs(grandChildTag));
|
|
if (grandChildTag == mTreeChildrenTag)
|
|
break;
|
|
}
|
|
if (j >= 0 && grandChild)
|
|
FindPreviousRowContent(aDelta, nsnull, grandChild, aResult);
|
|
|
|
if (aDelta == 0)
|
|
return;
|
|
}
|
|
|
|
// Descend into this row group and try to find a previous row.
|
|
FindPreviousRowContent(aDelta, nsnull, childContent, aResult);
|
|
if (aDelta == 0)
|
|
return;
|
|
}
|
|
}
|
|
|
|
NS_ASSERTION(parentContent, "Parent content null at the end of FPRC\n");
|
|
if (!parentContent)
|
|
return;
|
|
|
|
nsCOMPtr<nsIAtom> tag;
|
|
parentContent->GetTag(*getter_AddRefs(tag));
|
|
if (tag && tag.get() == nsXULAtoms::tree) {
|
|
// Hopeless. It ain't in there.
|
|
return;
|
|
}
|
|
else if (!aDownwardHint) // We didn't find it here. We need to go up to our parent, using ourselves as a hint.
|
|
FindPreviousRowContent(aDelta, parentContent, nsnull, aResult);
|
|
|
|
// Bail. There's nothing else we can do.
|
|
}
|
|
|
|
void
|
|
nsXULTreeOuterGroupFrame::FindNextRowContent(PRInt32& aDelta, nsIContent* aUpwardHint,
|
|
nsIContent* aDownwardHint,
|
|
nsIContent** aResult)
|
|
{
|
|
// Init to nsnull.
|
|
*aResult = nsnull;
|
|
|
|
// It disappoints me that this function is completely tied to the content nodes,
|
|
// but I can't see any other way to handle this. I don't have the frames, so I have nothing
|
|
// else to fall back on but the content nodes.
|
|
PRInt32 index = -1;
|
|
nsCOMPtr<nsIContent> parentContent;
|
|
if (aUpwardHint) {
|
|
aUpwardHint->GetParent(*getter_AddRefs(parentContent));
|
|
if (!parentContent) {
|
|
NS_ERROR("Parent content should not be NULL!");
|
|
return;
|
|
}
|
|
parentContent->IndexOf(aUpwardHint, index);
|
|
}
|
|
else if (aDownwardHint) {
|
|
parentContent = dont_QueryInterface(aDownwardHint);
|
|
}
|
|
|
|
PRInt32 childCount;
|
|
parentContent->ChildCount(childCount);
|
|
for (PRInt32 i = index+1; i < childCount; i++) {
|
|
nsCOMPtr<nsIContent> childContent;
|
|
parentContent->ChildAt(i, *getter_AddRefs(childContent));
|
|
nsCOMPtr<nsIAtom> tag;
|
|
childContent->GetTag(*getter_AddRefs(tag));
|
|
if (tag == mTreeRowTag) {
|
|
aDelta--;
|
|
if (aDelta == 0) {
|
|
*aResult = childContent;
|
|
NS_IF_ADDREF(*aResult);
|
|
return;
|
|
}
|
|
}
|
|
else if (tag == mTreeItemTag) {
|
|
// Descend into this row group and try to find a next row.
|
|
FindNextRowContent(aDelta, nsnull, childContent, aResult);
|
|
if (aDelta == 0)
|
|
return;
|
|
}
|
|
else if (tag == mTreeChildrenTag) {
|
|
// If it's open, descend into its treechildren node first.
|
|
nsCOMPtr<nsIAtom> openAtom = dont_AddRef(NS_NewAtom("open"));
|
|
nsAutoString isOpen;
|
|
parentContent->GetAttr(kNameSpaceID_None, openAtom, isOpen);
|
|
if (isOpen.Equals(NS_LITERAL_STRING("true"))) {
|
|
FindNextRowContent(aDelta, nsnull, childContent, aResult);
|
|
if (aDelta == 0)
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
nsCOMPtr<nsIAtom> tag;
|
|
parentContent->GetTag(*getter_AddRefs(tag));
|
|
if (tag && tag.get() == nsXULAtoms::tree) {
|
|
// Hopeless. It ain't in there.
|
|
return;
|
|
}
|
|
else if (!aDownwardHint) // We didn't find it here. We need to go up to our parent, using ourselves as a hint.
|
|
FindNextRowContent(aDelta, parentContent, nsnull, aResult);
|
|
|
|
// Bail. There's nothing else we can do.
|
|
}
|
|
|
|
void
|
|
nsXULTreeOuterGroupFrame::EnsureRowIsVisible(PRInt32 aRowIndex)
|
|
{
|
|
NS_ASSERTION(aRowIndex >= 0, "Ensure row is visible called with a negative number!");
|
|
if (aRowIndex < 0)
|
|
return;
|
|
|
|
PRInt32 rows = 0;
|
|
if (mRowHeight)
|
|
rows = GetAvailableHeight()/mRowHeight;
|
|
PRInt32 bottomIndex = mCurrentIndex + rows;
|
|
|
|
// if row is visible, ignore
|
|
if (mCurrentIndex <= aRowIndex && aRowIndex < bottomIndex)
|
|
return;
|
|
|
|
// Check to be sure we're not scrolling off the bottom of the tree
|
|
PRInt32 delta;
|
|
|
|
PRBool up = aRowIndex < mCurrentIndex;
|
|
if (up) {
|
|
delta = mCurrentIndex - aRowIndex;
|
|
mCurrentIndex = aRowIndex;
|
|
}
|
|
else {
|
|
// Bring it just into view.
|
|
delta = 1 + (aRowIndex-bottomIndex);
|
|
mCurrentIndex += delta;
|
|
}
|
|
|
|
InternalPositionChanged(up, delta);
|
|
}
|
|
|
|
void
|
|
nsXULTreeOuterGroupFrame::ScrollToIndex(PRInt32 aRowIndex, PRBool aForceDestruct)
|
|
{
|
|
if (( aRowIndex < 0 ) || (mRowHeight == 0))
|
|
return;
|
|
|
|
PRInt32 newIndex = aRowIndex;
|
|
PRInt32 delta = mCurrentIndex > newIndex ? mCurrentIndex - newIndex : newIndex - mCurrentIndex;
|
|
PRBool up = newIndex < mCurrentIndex;
|
|
|
|
// Check to be sure we're not scrolling off the bottom of the tree
|
|
PRInt32 lastPageTopRow = GetRowCount() - (GetAvailableHeight() / mRowHeight);
|
|
if (lastPageTopRow < 0)
|
|
lastPageTopRow = 0;
|
|
|
|
if (aRowIndex > lastPageTopRow)
|
|
return;
|
|
|
|
mCurrentIndex = newIndex;
|
|
InternalPositionChanged(up, delta, aForceDestruct);
|
|
|
|
// This change has to happen immediately.
|
|
// Flush any pending reflow commands.
|
|
nsCOMPtr<nsIDocument> doc;
|
|
mContent->GetDocument(*getter_AddRefs(doc));
|
|
doc->FlushPendingNotifications();
|
|
}
|
|
|
|
// walks the DOM to get the zero-based row index of the current content
|
|
// note that aContent can be any element, this will get the index of the
|
|
// element's parent
|
|
NS_IMETHODIMP
|
|
nsXULTreeOuterGroupFrame::IndexOfItem(nsIContent* aRoot, nsIContent* aContent,
|
|
PRBool aDescendIntoRows, // Invariant
|
|
PRBool aParentIsOpen,
|
|
PRInt32 *aResult)
|
|
{
|
|
PRInt32 childCount=0;
|
|
aRoot->ChildCount(childCount);
|
|
|
|
nsresult rv;
|
|
|
|
PRInt32 childIndex;
|
|
for (childIndex=0; childIndex<childCount; childIndex++) {
|
|
nsCOMPtr<nsIContent> child;
|
|
aRoot->ChildAt(childIndex, *getter_AddRefs(child));
|
|
|
|
nsCOMPtr<nsIAtom> childTag;
|
|
child->GetTag(*getter_AddRefs(childTag));
|
|
|
|
// is this it?
|
|
if (child.get() == aContent)
|
|
return NS_OK;
|
|
|
|
// we hit a treerow, count it
|
|
if (childTag == mTreeItemTag)
|
|
(*aResult)++;
|
|
|
|
PRBool descend = PR_TRUE;
|
|
PRBool parentIsOpen = aParentIsOpen;
|
|
|
|
// don't descend into closed children
|
|
if (childTag == mTreeChildrenTag && !parentIsOpen)
|
|
descend = PR_FALSE;
|
|
|
|
// speed optimization - descend into rows only when told
|
|
else if (childTag == mTreeRowTag && !aDescendIntoRows)
|
|
descend = PR_FALSE;
|
|
|
|
// descend as normally, but remember that the parent is closed!
|
|
else if (childTag == mTreeItemTag) {
|
|
nsAutoString isOpen;
|
|
rv = child->GetAttr(kNameSpaceID_None, nsXULAtoms::open, isOpen);
|
|
|
|
if (!isOpen.Equals(NS_LITERAL_STRING("true")))
|
|
parentIsOpen=PR_FALSE;
|
|
}
|
|
|
|
// now that we've analyzed the tags, recurse
|
|
if (descend) {
|
|
rv = IndexOfItem(child, aContent,
|
|
aDescendIntoRows, parentIsOpen, aResult);
|
|
if (NS_SUCCEEDED(rv))
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
// not found
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsXULTreeOuterGroupFrame::EndBatch()
|
|
{
|
|
NS_ASSERTION(mBatchCount, "EndBatch called on a tree that isn't batching!\n");
|
|
if (mBatchCount == 0)
|
|
return NS_OK;
|
|
|
|
mBatchCount--;
|
|
if (mBatchCount == 0) {
|
|
if (mCurrentIndex == mOldIndex) {
|
|
nsBoxLayoutState state(mPresContext);
|
|
MarkDirtyChildren(state);
|
|
}
|
|
else ScrollToIndex(mCurrentIndex, PR_TRUE);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsXULTreeOuterGroupFrame::ReflowFinished(nsIPresShell* aPresShell, PRBool* aFlushFlag)
|
|
{
|
|
// Now dirty the world.
|
|
nsCOMPtr<nsIContent> tree;
|
|
GetTreeContent(getter_AddRefs(tree));
|
|
|
|
nsIFrame* treeFrame;
|
|
aPresShell->GetPrimaryFrameFor(tree, &treeFrame);
|
|
|
|
nsCOMPtr<nsIBox> treeBox(do_QueryInterface(treeFrame));
|
|
|
|
nsBoxLayoutState state(mPresContext);
|
|
|
|
// now build or destroy any needed rows.
|
|
nsCOMPtr<nsIBoxLayout> layout;
|
|
GetLayoutManager(getter_AddRefs(layout));
|
|
nsTreeLayout* treeLayout = (nsTreeLayout*)layout.get();
|
|
treeLayout->LazyRowCreator(state, this);
|
|
|
|
if (mAdjustScroll) {
|
|
VerticalScroll(mYPosition);
|
|
mAdjustScroll = PR_FALSE;
|
|
}
|
|
|
|
// if the row height changed
|
|
// then mark everything as a style change. That
|
|
// will dirty the tree all the way to its leaves.
|
|
if (mRowHeightWasSet) {
|
|
if (!treeBox)
|
|
return NS_ERROR_NULL_POINTER;
|
|
treeBox->MarkStyleChange(state);
|
|
PRInt32 pos = mCurrentIndex*mRowHeight;
|
|
if (mYPosition != pos)
|
|
mAdjustScroll = PR_TRUE;
|
|
|
|
mRowHeightWasSet = PR_FALSE;
|
|
}
|
|
|
|
mReflowCallbackPosted = PR_FALSE;
|
|
|
|
*aFlushFlag = PR_TRUE;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
nsXULTreeOuterGroupFrame::RegenerateRowGroupInfo(PRBool aOnScreenCount)
|
|
{
|
|
NeedsRecalc();
|
|
|
|
PRInt32 oldRowCount = GetRowCount();
|
|
if (mRowGroupInfo)
|
|
mRowGroupInfo->Clear();
|
|
PRInt32 newRowCount = GetRowCount();
|
|
|
|
if (mRowHeight <= 0)
|
|
return;
|
|
|
|
// For removal only, we need to know how many rows are onscreen. These subtract
|
|
// from the amount that we need to adjust. For example, if the tree widget is
|
|
// scrolled to index 40, and if a folder that is offscreen at index 0
|
|
// is deleted, and it contains 9 kids, then a total of 10 rows are vanishing.
|
|
// newRowCount - oldRowCount will be -10 following the removal. However, if 4 of those
|
|
// 10 rows were onscreen, then the tree widget's index only needs to be adjusted by
|
|
// -6 (-10 + 4).
|
|
PRInt32 delta = newRowCount-oldRowCount+aOnScreenCount;
|
|
PRInt32 newIndex = mCurrentIndex + delta;
|
|
PRBool adjust = PR_FALSE;
|
|
|
|
// Check to be sure we're not scrolling off the bottom of the tree
|
|
PRInt32 lastPageTopRow = newRowCount - (GetAvailableHeight() / mRowHeight);
|
|
if (lastPageTopRow < 0) {
|
|
if (aOnScreenCount > 0)
|
|
adjust = PR_TRUE;
|
|
lastPageTopRow = 0;
|
|
}
|
|
|
|
if (newIndex > lastPageTopRow) {
|
|
newIndex = lastPageTopRow;
|
|
if (aOnScreenCount > 0)
|
|
adjust = PR_TRUE;
|
|
}
|
|
|
|
if (newIndex < 0)
|
|
newIndex = 0;
|
|
|
|
if (!adjust) {
|
|
if (mCurrentIndex == 0 || delta == 0)
|
|
return; // Just a simple update or we aren't scrolled, so bail.
|
|
|
|
nsCOMPtr<nsIContent> row;
|
|
GetFirstRowContent(getter_AddRefs(row));
|
|
NS_ASSERTION(row, "No row in regen check!");
|
|
if (!row)
|
|
return;
|
|
|
|
// An element was passed in that was either removed or added.
|
|
// We need to adjust our scroll position if this element's index
|
|
// is < our current scrolled index.
|
|
PRInt32 index = 0;
|
|
nsCOMPtr<nsIContent> item;
|
|
row->GetParent(*getter_AddRefs(item));
|
|
IndexOfItem(mContent, item, PR_FALSE, PR_TRUE, &index);
|
|
if (index == -1 || index == mCurrentIndex)
|
|
return;
|
|
}
|
|
|
|
// We mark the outer row group dirty and force a comprehensive
|
|
// rebuild of all tree widget frames. This ensures that we
|
|
// stay precisely in sync whenever we lose content from above.
|
|
if (IsBatching())
|
|
mCurrentIndex = newIndex;
|
|
else ScrollToIndex(newIndex, !adjust);
|
|
|
|
if (adjust) {
|
|
// Force a full redraw.
|
|
nsBoxLayoutState state(mPresContext);
|
|
Redraw(state, nsnull, PR_FALSE);
|
|
}
|
|
}
|
|
|
|
void
|
|
nsXULTreeOuterGroupFrame::GetTreeContent(nsIContent** aResult)
|
|
{
|
|
nsCOMPtr<nsIContent> content(mContent);
|
|
nsCOMPtr<nsIContent> bindingParent;
|
|
mContent->GetBindingParent(getter_AddRefs(bindingParent));
|
|
if (bindingParent) {
|
|
nsCOMPtr<nsIDocument> doc;
|
|
bindingParent->GetDocument(*getter_AddRefs(doc));
|
|
nsCOMPtr<nsIBindingManager> bindingManager;
|
|
doc->GetBindingManager(getter_AddRefs(bindingManager));
|
|
nsCOMPtr<nsIAtom> tag;
|
|
PRInt32 namespaceID;
|
|
bindingManager->ResolveTag(bindingParent, &namespaceID, getter_AddRefs(tag));
|
|
if (tag.get() == nsXULAtoms::tree) {
|
|
*aResult = bindingParent;
|
|
NS_ADDREF(*aResult);
|
|
}
|
|
}
|
|
else
|
|
mContent->GetParent(*aResult); // method does the addref
|
|
}
|
|
|
|
|
|
//
|
|
// Paint
|
|
//
|
|
// Overridden to handle the case where we should be drawing the tree as sorted.
|
|
//
|
|
NS_IMETHODIMP
|
|
nsXULTreeOuterGroupFrame :: Paint ( nsIPresContext* aPresContext, nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aDirtyRect, nsFramePaintLayer aWhichLayer)
|
|
{
|
|
nsresult res = NS_OK;
|
|
|
|
res = nsBoxFrame::Paint ( aPresContext, aRenderingContext, aDirtyRect, aWhichLayer );
|
|
|
|
if ( (aWhichLayer == eFramePaintLayer_Content) &&
|
|
(mYDropLoc != nsTreeItemDragCapturer::kNoDropLoc || mDropOnContainer || mTreeIsSorted) )
|
|
PaintDropFeedback ( aPresContext, aRenderingContext, PR_TRUE );
|
|
|
|
return res;
|
|
|
|
} // Paint
|
|
|
|
|
|
//
|
|
// AttributeChanged
|
|
//
|
|
// Track several attributes set by the d&d drop feedback tracking mechanism, notably
|
|
// telling us to paint as sorted
|
|
//
|
|
NS_IMETHODIMP
|
|
nsXULTreeOuterGroupFrame :: AttributeChanged ( nsIPresContext* aPresContext, nsIContent* aChild,
|
|
PRInt32 aNameSpaceID, nsIAtom* aAttribute,
|
|
PRInt32 aModType, PRInt32 aHint)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
|
|
if ( aAttribute == nsXULAtoms::ddTriggerRepaintSorted ) {
|
|
// Set a flag so that children won't draw the drop feedback but the parent
|
|
// will.
|
|
mTreeIsSorted = PR_TRUE;
|
|
ForceDrawFrame ( aPresContext, this );
|
|
mTreeIsSorted = PR_FALSE;
|
|
}
|
|
else
|
|
rv = nsXULTreeGroupFrame::AttributeChanged ( aPresContext, aChild, aNameSpaceID, aAttribute, aModType, aHint );
|
|
|
|
return rv;
|
|
|
|
} // AttributeChanged
|
|
|
|
|
|
|
|
#ifdef XP_MAC
|
|
#pragma mark -
|
|
#endif
|
|
|
|
|
|
|
|
//
|
|
// HandleAutoScrollTracking
|
|
//
|
|
//
|
|
nsresult
|
|
nsXULTreeOuterGroupFrame :: HandleAutoScrollTracking ( const nsPoint & aPoint )
|
|
{
|
|
#if USE_TIMER_TO_DELAY_SCROLLING
|
|
// if we're in the scroll region and there is no timer, start one and return
|
|
// if the timer has started but not yet fired, do nothing
|
|
// if the timer has fired, scroll the direction it tells us.
|
|
PRBool scrollUp = PR_FALSE;
|
|
if ( IsInDragScrollRegion(aPoint, &scrollUp) ) {
|
|
if ( mAutoScrollTimerStarted ) {
|
|
printf("waiting...\n");
|
|
if ( mAutoScrollTimerHasFired ) {
|
|
// we're in the right area and the timer has fired, so scroll
|
|
ScrollToIndex ( scrollUp ? mCurrentIndex - 1 : mCurrentIndex + 1);
|
|
}
|
|
}
|
|
else {
|
|
printf("starting timer\n");
|
|
// timer hasn't started yet, kick it off. Remember, this timer is a one-shot
|
|
mAutoScrollTimer = do_CreateInstance("@mozilla.org/timer;1");
|
|
if ( mAutoScrollTimer ) {
|
|
mAutoScrollTimer->Init(this, 100);
|
|
mAutoScrollTimerStarted = PR_TRUE;
|
|
}
|
|
else
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
} // we're in an area we care about
|
|
else
|
|
StopScrollTracking();
|
|
|
|
#else
|
|
|
|
PRBool scrollUp = PR_FALSE;
|
|
if ( IsInDragScrollRegion(aPoint, &scrollUp) )
|
|
ScrollToIndex ( scrollUp ? mCurrentIndex - 1 : mCurrentIndex + 1);
|
|
|
|
#endif
|
|
|
|
return NS_OK;
|
|
|
|
} // HandleAutoScrollTracking
|
|
|
|
|
|
//
|
|
// IsInDragScrollRegion
|
|
//
|
|
// Determine if the current point is inside one of the scroll regions near the top
|
|
// or bottom of the tree and will return PR_TRUE if it is, PR_FALSE if it isn't.
|
|
// If it is, this will set |outScrollUp| to PR_TRUE if the tree should scroll up,
|
|
// PR_FALSE if it should scroll down.
|
|
//
|
|
PRBool
|
|
nsXULTreeOuterGroupFrame :: IsInDragScrollRegion ( const nsPoint& inPoint, PRBool* outScrollUp )
|
|
{
|
|
PRBool isInRegion = PR_FALSE;
|
|
|
|
float pixelsToTwips = 0.0;
|
|
mPresContext->GetPixelsToTwips ( &pixelsToTwips );
|
|
nsPoint mouseInTwips ( NSToIntRound(inPoint.x * pixelsToTwips), NSToIntRound(inPoint.y * pixelsToTwips) );
|
|
|
|
// compute the offset to top level in twips and subtract the offset from
|
|
// the mouse coord to put it into our coordinates.
|
|
nscoord frameOffsetX = 0, frameOffsetY = 0;
|
|
nsIFrame* curr = this;
|
|
curr->GetParent(&curr);
|
|
while ( curr ) {
|
|
nsPoint origin;
|
|
curr->GetOrigin(origin); // in twips
|
|
frameOffsetX += origin.x; // build the offset incrementally
|
|
frameOffsetY += origin.y;
|
|
curr->GetParent(&curr); // moving up the chain
|
|
} // until we reach the top
|
|
mouseInTwips.MoveBy ( -frameOffsetX, -frameOffsetY );
|
|
|
|
const int kMarginHeight = NSToIntRound ( 12 * pixelsToTwips );
|
|
|
|
// scroll if we're inside the bounds of the tree and w/in |kMarginHeight|
|
|
// from the top or bottom of the tree.
|
|
if ( mRect.Contains(mouseInTwips) ) {
|
|
if ( mouseInTwips.y <= kMarginHeight ) {
|
|
isInRegion = PR_TRUE;
|
|
if ( outScrollUp )
|
|
*outScrollUp = PR_TRUE; // scroll up
|
|
}
|
|
else if ( mouseInTwips.y > GetAvailableHeight() - kMarginHeight ) {
|
|
isInRegion = PR_TRUE;
|
|
if ( outScrollUp )
|
|
*outScrollUp = PR_FALSE; // scroll down
|
|
}
|
|
}
|
|
|
|
return isInRegion;
|
|
|
|
} // IsInDragScrollRegion
|
|
|
|
|
|
#if USE_TIMER_TO_DELAY_SCROLLING
|
|
|
|
//
|
|
// Notify
|
|
//
|
|
// Called when the timer fires. Tells the tree that we're ready to scroll if the
|
|
// mouse is in the right position.
|
|
//
|
|
void
|
|
nsXULTreeOuterGroupFrame :: Notify ( nsITimer* timer )
|
|
{
|
|
printf("000 FIRE\n");
|
|
mAutoScrollTimerHasFired = PR_TRUE;
|
|
}
|
|
|
|
nsresult
|
|
nsXULTreeOuterGroupFrame :: StopScrollTracking()
|
|
{
|
|
printf("--stop\n");
|
|
if ( mAutoScrollTimer && mAutoScrollTimerStarted )
|
|
mAutoScrollTimer->Cancel();
|
|
mAutoScrollTimerHasFired = PR_FALSE;
|
|
mAutoScrollTimerStarted = PR_FALSE;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
#endif
|
|
|
|
NS_IMETHODIMP
|
|
nsXULTreeOuterGroupFrame::SizeTo(nsIPresContext* aPresContext, nscoord aWidth, nscoord aHeight)
|
|
{
|
|
return nsXULTreeGroupFrame::SizeTo(aPresContext, aWidth, aHeight);
|
|
}
|
|
|
|
class nsTreeScrollPortFrame : public nsScrollPortFrame
|
|
{
|
|
public:
|
|
nsTreeScrollPortFrame(nsIPresShell* aShell);
|
|
friend nsresult NS_NewScrollBoxFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame);
|
|
NS_IMETHOD GetPrefSize(nsBoxLayoutState& aBoxLayoutState, nsSize& aSize);
|
|
NS_IMETHOD GetMinSize(nsBoxLayoutState& aBoxLayoutState, nsSize& aSize);
|
|
};
|
|
|
|
// Scrollport Subclass
|
|
nsresult
|
|
NS_NewTreeScrollPortFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame)
|
|
{
|
|
NS_PRECONDITION(aNewFrame, "null OUT ptr");
|
|
if (nsnull == aNewFrame) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
nsTreeScrollPortFrame* it = new (aPresShell) nsTreeScrollPortFrame (aPresShell);
|
|
if (nsnull == it) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
*aNewFrame = it;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsTreeScrollPortFrame::nsTreeScrollPortFrame(nsIPresShell* aShell):nsScrollPortFrame(aShell)
|
|
{
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTreeScrollPortFrame::GetMinSize(nsBoxLayoutState& aBoxLayoutState, nsSize& aSize)
|
|
{
|
|
nsIBox* child = nsnull;
|
|
GetChildBox(&child);
|
|
|
|
nsresult rv = child->GetPrefSize(aBoxLayoutState, aSize);
|
|
nsXULTreeOuterGroupFrame* outer = NS_STATIC_CAST(nsXULTreeOuterGroupFrame*,child);
|
|
|
|
nsAutoString sizeMode;
|
|
nsCOMPtr<nsIContent> content;
|
|
outer->GetContent(getter_AddRefs(content));
|
|
content->GetAttr(kNameSpaceID_None, nsXULAtoms::sizemode, sizeMode);
|
|
if (!sizeMode.IsEmpty()) {
|
|
nsCOMPtr<nsIScrollableFrame> scrollFrame(do_QueryInterface(mParent));
|
|
if (scrollFrame) {
|
|
nsIScrollableFrame::nsScrollPref scrollPref;
|
|
scrollFrame->GetScrollPreference(aBoxLayoutState.GetPresContext(), &scrollPref);
|
|
|
|
if (scrollPref == nsIScrollableFrame::Auto) {
|
|
nscoord vbarwidth, hbarheight;
|
|
scrollFrame->GetScrollbarSizes(aBoxLayoutState.GetPresContext(),
|
|
&vbarwidth, &hbarheight);
|
|
aSize.width += vbarwidth;
|
|
}
|
|
}
|
|
}
|
|
else aSize.width = 0;
|
|
|
|
aSize.height = 0;
|
|
|
|
AddMargin(child, aSize);
|
|
AddBorderAndPadding(aSize);
|
|
AddInset(aSize);
|
|
nsIBox::AddCSSMinSize(aBoxLayoutState, this, aSize);
|
|
return rv;
|
|
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTreeScrollPortFrame::GetPrefSize(nsBoxLayoutState& aBoxLayoutState, nsSize& aSize)
|
|
{
|
|
nsIBox* child = nsnull;
|
|
GetChildBox(&child);
|
|
|
|
nsresult rv = child->GetPrefSize(aBoxLayoutState, aSize);
|
|
nsXULTreeOuterGroupFrame* outer = NS_STATIC_CAST(nsXULTreeOuterGroupFrame*,child);
|
|
|
|
PRInt32 size = outer->GetFixedRowSize();
|
|
|
|
if (size > -1)
|
|
aSize.height = size*outer->GetRowHeightTwips();
|
|
|
|
nsCOMPtr<nsIScrollableFrame> scrollFrame(do_QueryInterface(mParent));
|
|
if (scrollFrame) {
|
|
nsIScrollableFrame::nsScrollPref scrollPref;
|
|
scrollFrame->GetScrollPreference(aBoxLayoutState.GetPresContext(), &scrollPref);
|
|
|
|
if (scrollPref == nsIScrollableFrame::Auto) {
|
|
nscoord vbarwidth, hbarheight;
|
|
scrollFrame->GetScrollbarSizes(aBoxLayoutState.GetPresContext(),
|
|
&vbarwidth, &hbarheight);
|
|
aSize.width += vbarwidth;
|
|
}
|
|
}
|
|
|
|
AddMargin(child, aSize);
|
|
AddBorderAndPadding(aSize);
|
|
AddInset(aSize);
|
|
nsIBox::AddCSSPrefSize(aBoxLayoutState, this, aSize);
|
|
return rv;
|
|
|
|
}
|
|
|
|
nscoord
|
|
nsXULTreeOuterGroupFrame::ComputeIntrinsicWidth(nsBoxLayoutState& aBoxLayoutState)
|
|
{
|
|
if (mStringWidth != -1)
|
|
return mStringWidth;
|
|
|
|
nscoord largestWidth = 0;
|
|
|
|
nsCOMPtr<nsIContent> firstRowContent;
|
|
nsCOMPtr<nsIContent> content;
|
|
GetTreeContent(getter_AddRefs(content));
|
|
PRInt32 index = 0;
|
|
FindRowContentAtIndex(index, content, getter_AddRefs(firstRowContent));
|
|
|
|
if (firstRowContent) {
|
|
nsCOMPtr<nsIStyleContext> styleContext;
|
|
aBoxLayoutState.GetPresContext()->ResolveStyleContextFor(firstRowContent, nsnull,
|
|
PR_FALSE,
|
|
getter_AddRefs(styleContext));
|
|
|
|
nscoord width = 0;
|
|
nsMargin margin(0,0,0,0);
|
|
|
|
nsStyleBorderPadding bPad;
|
|
styleContext->GetBorderPaddingFor(bPad);
|
|
bPad.GetBorderPadding(margin);
|
|
|
|
width += (margin.left + margin.right);
|
|
|
|
const nsStyleMargin* styleMargin = (const nsStyleMargin*)styleContext->GetStyleData(eStyleStruct_Margin);
|
|
styleMargin->GetMargin(margin);
|
|
width += (margin.left + margin.right);
|
|
|
|
nsCOMPtr<nsIContent> content;
|
|
GetTreeContent(getter_AddRefs(content));
|
|
|
|
PRInt32 childCount;
|
|
content->ChildCount(childCount);
|
|
|
|
nsCOMPtr<nsIContent> child;
|
|
for (PRInt32 i = 0; i < childCount && i < 100; ++i) {
|
|
content->ChildAt(i, *getter_AddRefs(child));
|
|
|
|
nsCOMPtr<nsIAtom> tag;
|
|
child->GetTag(*getter_AddRefs(tag));
|
|
if (tag == mTreeRowTag) {
|
|
nsIPresContext* presContext = aBoxLayoutState.GetPresContext();
|
|
nsIRenderingContext* rendContext = aBoxLayoutState.GetReflowState()->rendContext;
|
|
if (rendContext) {
|
|
nsAutoString value;
|
|
nsCOMPtr<nsIContent> textChild;
|
|
PRInt32 textCount;
|
|
child->ChildCount(textCount);
|
|
for (PRInt32 j = 0; j < textCount; ++j) {
|
|
child->ChildAt(j, *getter_AddRefs(textChild));
|
|
nsCOMPtr<nsIDOMText> text(do_QueryInterface(textChild));
|
|
if (text) {
|
|
nsAutoString data;
|
|
text->GetData(data);
|
|
value += data;
|
|
}
|
|
}
|
|
const nsStyleFont* font = (const nsStyleFont*)styleContext->GetStyleData(eStyleStruct_Font);
|
|
nsCOMPtr<nsIDeviceContext> dc;
|
|
presContext->GetDeviceContext(getter_AddRefs(dc));
|
|
nsCOMPtr<nsIFontMetrics> fm;
|
|
dc->GetMetricsFor(font->mFont, *getter_AddRefs(fm));
|
|
rendContext->SetFont(fm);
|
|
|
|
nscoord textWidth;
|
|
rendContext->GetWidth(value, textWidth);
|
|
textWidth += width;
|
|
|
|
if (textWidth > largestWidth)
|
|
largestWidth = textWidth;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
mStringWidth = largestWidth;
|
|
return mStringWidth;
|
|
}
|