fix oncontentinserted and oncontentremoved so that mTopFrame is set correctly, and don't be over-zealous about destroying frames. r=hyatt (well, really written mostly by hyatt and digested by me) git-svn-id: svn://10.0.0.236/trunk@55745 18797224-902f-48f8-a5cc-f745e15eee43
1924 lines
60 KiB
C++
1924 lines
60 KiB
C++
/* -*- Mode: C++; tab-width: 4; 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 Communicator client code.
|
|
*
|
|
* The Initial Developer of the Original Code is Netscape Communications
|
|
* Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*/
|
|
|
|
#include "nsCOMPtr.h"
|
|
#include "nsXULAtoms.h"
|
|
#include "nsHTMLAtoms.h"
|
|
#include "nsINameSpaceManager.h"
|
|
#include "nsTreeFrame.h"
|
|
#include "nsIPresContext.h"
|
|
#include "nsIPresShell.h"
|
|
#include "nsTreeRowGroupFrame.h"
|
|
#include "nsIStyleContext.h"
|
|
#include "nsCSSFrameConstructor.h"
|
|
#include "nsIContent.h"
|
|
#include "nsCSSRendering.h"
|
|
#include "nsTreeCellFrame.h"
|
|
#include "nsCellMap.h"
|
|
#include "nsIReflowCommand.h"
|
|
#include "nsHTMLParts.h"
|
|
#include "nsScrollbarButtonFrame.h"
|
|
#include "nsSliderFrame.h"
|
|
#include "nsIDOMElement.h"
|
|
#include "nsISupportsArray.h"
|
|
#include "nsIDocument.h"
|
|
#include "nsIDOMDocument.h"
|
|
#include "nsIDOMEventReceiver.h"
|
|
#include "nsIDOMDragListener.h"
|
|
#include "nsTreeItemDragCapturer.h"
|
|
|
|
static nsILayoutHistoryState*
|
|
GetStateStorageObject(nsTreeRowGroupFrame* aTreeRowGroupFrame,
|
|
nsTableFrame* aTableFrame);
|
|
|
|
|
|
// define this to get some help
|
|
#undef DEBUG_tree
|
|
|
|
//
|
|
// NS_NewTreeFrame
|
|
//
|
|
// Creates a new tree frame
|
|
//
|
|
nsresult
|
|
NS_NewTreeRowGroupFrame (nsIPresShell* aPresShell, nsIFrame** aNewFrame)
|
|
{
|
|
NS_PRECONDITION(aNewFrame, "null OUT ptr");
|
|
if (nsnull == aNewFrame) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
nsTreeRowGroupFrame* it = new (aPresShell) nsTreeRowGroupFrame;
|
|
if (!it)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
*aNewFrame = it;
|
|
return NS_OK;
|
|
|
|
} // NS_NewTreeFrame
|
|
|
|
|
|
// Constructor
|
|
nsTreeRowGroupFrame::nsTreeRowGroupFrame()
|
|
: nsTableRowGroupFrame(), mTopFrame(nsnull), mBottomFrame(nsnull),
|
|
mLinkupFrame(nsnull), mIsLazy(PR_FALSE), mIsFull(PR_FALSE),
|
|
mScrollbar(nsnull), mShouldHaveScrollbar(PR_FALSE),
|
|
mContentChain(nsnull), mFrameConstructor(nsnull),
|
|
mRowGroupHeight(0), mCurrentIndex(0), mRowCount(0),
|
|
mYDropLoc(-1), mDropOnContainer(PR_FALSE)
|
|
{ }
|
|
|
|
// Destructor
|
|
nsTreeRowGroupFrame::~nsTreeRowGroupFrame()
|
|
{
|
|
nsCOMPtr<nsIContent> content;
|
|
GetContent(getter_AddRefs(content));
|
|
nsCOMPtr<nsIDOMEventReceiver> receiver(do_QueryInterface(content));
|
|
|
|
// NOTE: the last Remove will delete the drag capturer
|
|
if ( receiver ) {
|
|
receiver->RemoveEventListener("dragover", mDragCapturer, PR_TRUE);
|
|
receiver->RemoveEventListener("dragexit", mDragCapturer, PR_TRUE);
|
|
}
|
|
|
|
NS_IF_RELEASE(mContentChain);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTreeRowGroupFrame::Destroy(nsIPresContext* aPresContext)
|
|
{
|
|
if (mScrollbar) {
|
|
mFrameConstructor->RemoveMappingsForFrameSubtree(aPresContext, mScrollbar, nsnull);
|
|
mScrollbar->Destroy(aPresContext);
|
|
}
|
|
return nsTableRowGroupFrame::Destroy(aPresContext);
|
|
}
|
|
|
|
nsrefcnt nsTreeRowGroupFrame::AddRef(void)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
nsrefcnt nsTreeRowGroupFrame::Release(void)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTreeRowGroupFrame::QueryInterface(REFNSIID aIID, void** aInstancePtr)
|
|
{
|
|
if (NULL == aInstancePtr) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
|
|
*aInstancePtr = NULL;
|
|
|
|
if (aIID.Equals(nsIScrollbarListener::GetIID())) {
|
|
*aInstancePtr = (void*)(nsIScrollbarListener*) this;
|
|
NS_ADDREF_THIS();
|
|
return NS_OK;
|
|
}
|
|
|
|
return nsTableRowGroupFrame::QueryInterface(aIID, aInstancePtr);
|
|
}
|
|
|
|
|
|
//
|
|
// Init
|
|
//
|
|
// Setup event capturers for drag and drop. 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 drag capturer 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.
|
|
//
|
|
NS_IMETHODIMP
|
|
nsTreeRowGroupFrame::Init ( nsIPresContext* aPresContext, nsIContent* aContent,
|
|
nsIFrame* aParent, nsIStyleContext* aContext, nsIFrame* aPrevInFlow)
|
|
{
|
|
nsresult rv = nsTableRowGroupFrame::Init(aPresContext, aContent, aParent, aContext, aPrevInFlow);
|
|
if ( NS_SUCCEEDED(rv) ) {
|
|
nsCOMPtr<nsIContent> content;
|
|
GetContent(getter_AddRefs(content));
|
|
nsCOMPtr<nsIDOMEventReceiver> receiver(do_QueryInterface(content));
|
|
|
|
// register our drag over and exit capturers. These annotate the content object
|
|
// with enough info to determine where the drop would happen so that JS down the
|
|
// line can do the right thing.
|
|
mDragCapturer = new nsTreeItemDragCapturer(this, aPresContext);
|
|
receiver->AddEventListener("dragover", mDragCapturer, PR_TRUE);
|
|
receiver->AddEventListener("dragexit", mDragCapturer, PR_TRUE);
|
|
}
|
|
|
|
return rv;
|
|
|
|
} // Init
|
|
|
|
|
|
void nsTreeRowGroupFrame::DestroyRows(nsTableFrame* aTableFrame, nsIPresContext* aPresContext, PRInt32& rowsToLose)
|
|
{
|
|
// We need to destroy frames until our row count has been properly
|
|
// reduced. A reflow will then pick up and create the new frames.
|
|
nsIFrame* childFrame = GetFirstFrame();
|
|
while (childFrame && rowsToLose > 0) {
|
|
if (IsTableRowGroupFrame(childFrame))
|
|
{
|
|
PRInt32 rowGroupCount;
|
|
((nsTreeRowGroupFrame*)childFrame)->GetRowCount(rowGroupCount);
|
|
if ((rowGroupCount - rowsToLose) > 0) {
|
|
// The row group will destroy as many rows as it can, and it will
|
|
// modify rowsToLose.
|
|
((nsTreeRowGroupFrame*)childFrame)->DestroyRows(aTableFrame, aPresContext, rowsToLose);
|
|
return;
|
|
}
|
|
else
|
|
((nsTreeRowGroupFrame*)childFrame)->DestroyRows(aTableFrame, aPresContext, rowsToLose);
|
|
}
|
|
else if (IsTableRowFrame(childFrame))
|
|
{
|
|
// Lost a row.
|
|
rowsToLose--;
|
|
|
|
// Remove this row from our cell map.
|
|
nsTableRowFrame* rowFrame = (nsTableRowFrame*)childFrame;
|
|
aTableFrame->RemoveRowFromMap(rowFrame, rowFrame->GetRowIndex());
|
|
}
|
|
|
|
nsIFrame* nextFrame;
|
|
GetNextFrame(childFrame, &nextFrame);
|
|
mFrameConstructor->RemoveMappingsForFrameSubtree(aPresContext, childFrame, GetStateStorageObject(this, aTableFrame));
|
|
mFrames.DestroyFrame(aPresContext, childFrame);
|
|
mTopFrame = childFrame = nextFrame;
|
|
}
|
|
}
|
|
|
|
void nsTreeRowGroupFrame::ReverseDestroyRows(nsTableFrame* aTableFrame, nsIPresContext* aPresContext, PRInt32& rowsToLose)
|
|
{
|
|
// We need to destroy frames until our row count has been properly
|
|
// reduced. A reflow will then pick up and create the new frames.
|
|
nsIFrame* childFrame = GetLastFrame();
|
|
while (childFrame && rowsToLose > 0) {
|
|
if (IsTableRowGroupFrame(childFrame))
|
|
{
|
|
PRInt32 rowGroupCount;
|
|
((nsTreeRowGroupFrame*)childFrame)->GetRowCount(rowGroupCount);
|
|
if ((rowGroupCount - rowsToLose) > 0) {
|
|
// The row group will destroy as many rows as it can, and it will
|
|
// modify rowsToLose.
|
|
((nsTreeRowGroupFrame*)childFrame)->ReverseDestroyRows(aTableFrame, aPresContext, rowsToLose);
|
|
return;
|
|
}
|
|
else
|
|
((nsTreeRowGroupFrame*)childFrame)->ReverseDestroyRows(aTableFrame, aPresContext, rowsToLose);
|
|
}
|
|
else if (IsTableRowFrame(childFrame))
|
|
{
|
|
// Lost a row.
|
|
rowsToLose--;
|
|
// Remove this row from our cell map.
|
|
nsTableRowFrame* rowFrame = (nsTableRowFrame*)childFrame;
|
|
aTableFrame->RemoveRowFromMap(rowFrame, rowFrame->GetRowIndex());
|
|
}
|
|
|
|
nsIFrame* prevFrame;
|
|
prevFrame = mFrames.GetPrevSiblingFor(childFrame);
|
|
mFrameConstructor->RemoveMappingsForFrameSubtree(aPresContext, childFrame, GetStateStorageObject(this, aTableFrame));
|
|
mFrames.DestroyFrame(aPresContext, childFrame);
|
|
mBottomFrame = childFrame = prevFrame;
|
|
}
|
|
}
|
|
|
|
void
|
|
nsTreeRowGroupFrame::ConstructContentChain(nsIContent* aRowContent)
|
|
{
|
|
// Create the content chain array.
|
|
NS_IF_RELEASE(mContentChain);
|
|
NS_NewISupportsArray(&mContentChain);
|
|
|
|
// Move up the chain until we hit our content node.
|
|
nsCOMPtr<nsIContent> currContent = dont_QueryInterface(aRowContent);
|
|
while (currContent && (currContent.get() != mContent)) {
|
|
mContentChain->InsertElementAt(currContent, 0);
|
|
nsCOMPtr<nsIContent> otherContent = currContent;
|
|
otherContent->GetParent(*getter_AddRefs(currContent));
|
|
}
|
|
|
|
NS_ASSERTION(currContent.get() == mContent, "Disaster! Content not contained in our tree!\n");
|
|
}
|
|
|
|
void
|
|
nsTreeRowGroupFrame::ConstructOldContentChain(nsIPresContext* aPresContext,
|
|
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(aPresContext, aOldRowContent, childOfCommonAncestor);
|
|
}
|
|
}
|
|
|
|
void
|
|
nsTreeRowGroupFrame::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
|
|
nsTreeRowGroupFrame::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;
|
|
}
|
|
|
|
PRBool
|
|
nsTreeRowGroupFrame::IsTableRowGroupFrame(nsIFrame *frame)
|
|
{
|
|
if (!frame) return PR_FALSE;
|
|
|
|
const nsStyleDisplay *display;
|
|
frame->GetStyleData(eStyleStruct_Display,
|
|
(const nsStyleStruct*&)display);
|
|
|
|
return (display->mDisplay == NS_STYLE_DISPLAY_TABLE_ROW_GROUP);
|
|
}
|
|
|
|
PRBool
|
|
nsTreeRowGroupFrame::IsTableRowFrame(nsIFrame *frame)
|
|
{
|
|
if (!frame) return PR_FALSE;
|
|
|
|
const nsStyleDisplay *display;
|
|
frame->GetStyleData(eStyleStruct_Display,
|
|
(const nsStyleStruct*&)display);
|
|
|
|
return (display->mDisplay == NS_STYLE_DISPLAY_TABLE_ROW);
|
|
}
|
|
|
|
void
|
|
nsTreeRowGroupFrame::CreateOldContentChain(nsIPresContext* aPresContext, nsIContent* aOldRowContent, nsIContent* topOfChain)
|
|
{
|
|
nsCOMPtr<nsIContent> currContent = dont_QueryInterface(aOldRowContent);
|
|
nsCOMPtr<nsIContent> prevContent;
|
|
|
|
nsCOMPtr<nsIPresShell> shell;
|
|
aPresContext->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)
|
|
{
|
|
if (IsTableRowGroupFrame(primaryFrame))
|
|
{
|
|
//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));
|
|
|
|
nsTreeRowGroupFrame *curRowGroup = (nsTreeRowGroupFrame*)primaryFrame;
|
|
nsIFrame *parentFrame;
|
|
curRowGroup->GetParent(&parentFrame);
|
|
|
|
if (IsTableRowGroupFrame(parentFrame))
|
|
{
|
|
//Get the current content's parent's first frame.
|
|
nsTreeRowGroupFrame *parentRowGroupFrame =
|
|
(nsTreeRowGroupFrame*)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
|
|
nsTreeRowGroupFrame::GetFirstRowContent(nsIContent** aResult)
|
|
{
|
|
*aResult = nsnull;
|
|
nsIFrame* kid = GetFirstFrame();
|
|
while (kid) {
|
|
if (IsTableRowGroupFrame(kid))
|
|
{
|
|
((nsTreeRowGroupFrame*)kid)->GetFirstRowContent(aResult);
|
|
if (*aResult)
|
|
return;
|
|
}
|
|
else if (IsTableRowFrame(kid))
|
|
{
|
|
kid->GetContent(aResult); // The ADDREF happens here.
|
|
return;
|
|
}
|
|
GetNextFrame(kid, &kid);
|
|
}
|
|
}
|
|
|
|
void
|
|
nsTreeRowGroupFrame::FindRowContentAtIndex(PRInt32& aIndex,
|
|
nsIContent* aParent,
|
|
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 childCount;
|
|
aParent->ChildCount(childCount);
|
|
|
|
for (PRInt32 i = 0; i < childCount; i++) {
|
|
nsCOMPtr<nsIContent> childContent;
|
|
aParent->ChildAt(i, *getter_AddRefs(childContent));
|
|
nsCOMPtr<nsIAtom> tag;
|
|
childContent->GetTag(*getter_AddRefs(tag));
|
|
|
|
// treerow - Count this row, if we're at 0, then we've found the row.
|
|
if (tag.get() == nsXULAtoms::treerow) {
|
|
aIndex--;
|
|
if (aIndex < 0) {
|
|
*aResult = childContent;
|
|
NS_IF_ADDREF(*aResult);
|
|
return;
|
|
}
|
|
}
|
|
else if (tag.get() == nsXULAtoms::treeitem) {
|
|
// Descend into this row group and try to find the next row.
|
|
FindRowContentAtIndex(aIndex, childContent, aResult);
|
|
if (aIndex < 0)
|
|
return;
|
|
|
|
// If it's open, descend into its treechildren.
|
|
nsCOMPtr<nsIAtom> openAtom = dont_AddRef(NS_NewAtom("open"));
|
|
nsAutoString isOpen;
|
|
childContent->GetAttribute(kNameSpaceID_None, openAtom, isOpen);
|
|
if (isOpen == "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.get() == nsXULAtoms::treechildren)
|
|
break;
|
|
}
|
|
if (j >= 0 && grandChild)
|
|
FindRowContentAtIndex(aIndex, grandChild, aResult);
|
|
|
|
if (aIndex < 0)
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
nsTreeRowGroupFrame::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));
|
|
if (!parentContent) {
|
|
NS_ERROR("Parent content should not be NULL!");
|
|
return;
|
|
}
|
|
parentContent->IndexOf(aUpwardHint, index);
|
|
}
|
|
else if (aDownwardHint) {
|
|
parentContent = dont_QueryInterface(aDownwardHint);
|
|
parentContent->ChildCount(index);
|
|
}
|
|
|
|
/* Let me see inside the damn nsCOMptrs
|
|
nsIAtom* aAtom;
|
|
parentContent->GetTag(aAtom);
|
|
nsAutoString result;
|
|
aAtom->ToString(result);
|
|
*/
|
|
|
|
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.get() == nsXULAtoms::treerow) {
|
|
aDelta--;
|
|
if (aDelta == 0) {
|
|
*aResult = childContent;
|
|
NS_IF_ADDREF(*aResult);
|
|
return;
|
|
}
|
|
}
|
|
else if (tag.get() == nsXULAtoms::treeitem) {
|
|
// If it's open, descend into its treechildren node first.
|
|
nsCOMPtr<nsIAtom> openAtom = dont_AddRef(NS_NewAtom("open"));
|
|
nsAutoString isOpen;
|
|
childContent->GetAttribute(kNameSpaceID_None, openAtom, isOpen);
|
|
if (isOpen == "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.get() == nsXULAtoms::treechildren)
|
|
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;
|
|
}
|
|
}
|
|
|
|
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
|
|
nsTreeRowGroupFrame::ComputeTotalRowCount(PRInt32& aCount, nsIContent* aParent)
|
|
{
|
|
// XXX Check for visibility collapse and for display none on all objects.
|
|
// ARGH!
|
|
PRInt32 childCount;
|
|
aParent->ChildCount(childCount);
|
|
|
|
for (PRInt32 i = 0; i < childCount; i++) {
|
|
nsCOMPtr<nsIContent> childContent;
|
|
aParent->ChildAt(i, *getter_AddRefs(childContent));
|
|
nsCOMPtr<nsIAtom> tag;
|
|
childContent->GetTag(*getter_AddRefs(tag));
|
|
if (tag.get() == nsXULAtoms::treerow) {
|
|
aCount++;
|
|
}
|
|
else if (tag.get() == nsXULAtoms::treeitem) {
|
|
// Descend into this row group and try to find the next row.
|
|
ComputeTotalRowCount(aCount, childContent);
|
|
}
|
|
else if (tag.get() == nsXULAtoms::treechildren) {
|
|
// 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->GetAttribute(kNameSpaceID_None, openAtom, isOpen);
|
|
if (isOpen == "true")
|
|
ComputeTotalRowCount(aCount, childContent);
|
|
}
|
|
}
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTreeRowGroupFrame::PositionChanged(nsIPresContext* aPresContext, PRInt32 aOldIndex, PRInt32 aNewIndex)
|
|
{
|
|
#ifdef DEBUG_tree
|
|
printf("PositionChanged from %d to %d\n", aOldIndex, aNewIndex);
|
|
#endif
|
|
if (aNewIndex < 0)
|
|
return NS_OK;
|
|
|
|
if (aOldIndex == aNewIndex)
|
|
return NS_OK;
|
|
|
|
mCurrentIndex = aNewIndex;
|
|
|
|
if (mContentChain) {
|
|
NS_ERROR("This is bad!");
|
|
return NS_OK;
|
|
}
|
|
|
|
// Get our row count.
|
|
PRInt32 rowCount;
|
|
GetRowCount(rowCount);
|
|
|
|
// Get our owning tree.
|
|
nsTableFrame* tableFrame;
|
|
nsTableFrame::GetTableFrame(this, tableFrame);
|
|
|
|
// Figure out how many rows we need to lose (if we moved down) or gain (if we moved up).
|
|
PRInt32 delta = aNewIndex > aOldIndex ? aNewIndex - aOldIndex : aOldIndex - aNewIndex;
|
|
|
|
#ifdef DEBUG_tree
|
|
printf("Scrolling, the delta is: %d\n", delta);
|
|
#endif
|
|
|
|
// Get our presentation context.
|
|
if (delta < rowCount) {
|
|
PRInt32 loseRows = delta;
|
|
|
|
// scrolling down
|
|
if (aNewIndex > aOldIndex) {
|
|
// Figure out how many rows we have to lose off the top.
|
|
DestroyRows(tableFrame, aPresContext, 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(tableFrame, aPresContext, 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;
|
|
|
|
FindPreviousRowContent(delta, 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(aPresContext, 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.
|
|
mFrameConstructor->RemoveMappingsForFrameSubtree(aPresContext, this, GetStateStorageObject(this, tableFrame));
|
|
mFrames.DestroyFrames(aPresContext);
|
|
tableFrame->InvalidateCellMap();
|
|
nsCOMPtr<nsIContent> topRowContent;
|
|
FindRowContentAtIndex(aNewIndex, mContent, getter_AddRefs(topRowContent));
|
|
|
|
if (topRowContent)
|
|
ConstructContentChain(topRowContent);
|
|
}
|
|
|
|
mTopFrame = mBottomFrame = nsnull; // Make sure everything is cleared out.
|
|
|
|
// Force a reflow.
|
|
OnContentAdded(aPresContext);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTreeRowGroupFrame::PagedUpDown()
|
|
{
|
|
// Set the scrollbar's pageincrement
|
|
if (mScrollbar) {
|
|
PRInt32 rowGroupCount;
|
|
GetRowCount(rowGroupCount);
|
|
|
|
nsCOMPtr<nsIContent> scrollbarContent;
|
|
mScrollbar->GetContent(getter_AddRefs(scrollbarContent));
|
|
|
|
rowGroupCount--;
|
|
char ch[100];
|
|
sprintf(ch,"%d", rowGroupCount);
|
|
|
|
#ifdef DEBUG_tree
|
|
printf("PagedUpDown, setting increment to %d\n", rowGroupCount);
|
|
#endif
|
|
scrollbarContent->SetAttribute(kNameSpaceID_None, nsXULAtoms::pageincrement, nsAutoString(ch), PR_FALSE);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
nsTreeRowGroupFrame::SetScrollbarFrame(nsIFrame* aFrame)
|
|
{
|
|
mScrollbar = aFrame;
|
|
nsFrameList frameList(mScrollbar);
|
|
|
|
// Place it in its own list.
|
|
mScrollbarList.AppendFrames(this,frameList);
|
|
|
|
nsCOMPtr<nsIContent> scrollbarContent;
|
|
aFrame->GetContent(getter_AddRefs(scrollbarContent));
|
|
|
|
scrollbarContent->SetAttribute(kNameSpaceID_None, nsXULAtoms::curpos, "0", PR_FALSE);
|
|
scrollbarContent->SetAttribute(kNameSpaceID_None, nsXULAtoms::increment, "1", PR_FALSE);
|
|
scrollbarContent->SetAttribute(kNameSpaceID_None, nsXULAtoms::pageincrement, "1", PR_FALSE);
|
|
scrollbarContent->SetAttribute(kNameSpaceID_None, nsXULAtoms::maxpos, "5000", PR_FALSE);
|
|
|
|
nsIFrame* result;
|
|
nsScrollbarButtonFrame::GetChildWithTag(nsXULAtoms::slider, aFrame, result);
|
|
((nsSliderFrame*)result)->SetScrollbarListener(this);
|
|
}
|
|
|
|
PRBool nsTreeRowGroupFrame::RowGroupDesiresExcessSpace()
|
|
{
|
|
nsIFrame* parentFrame;
|
|
GetParent(&parentFrame);
|
|
if (IsTableRowGroupFrame(parentFrame))
|
|
return PR_FALSE; // Nested groups don't grow.
|
|
else return PR_TRUE; // The outermost group can grow.
|
|
}
|
|
|
|
PRBool nsTreeRowGroupFrame::RowGroupReceivesExcessSpace()
|
|
{
|
|
if (IsTableRowGroupFrame(this))
|
|
return PR_TRUE;
|
|
else return PR_FALSE; // Headers and footers don't get excess space.
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTreeRowGroupFrame::GetFrameForPoint(nsIPresContext* aPresContext,
|
|
const nsPoint& aPoint,
|
|
nsIFrame** aFrame)
|
|
{
|
|
nsPoint tmp;
|
|
nsRect kidRect;
|
|
if (mScrollbar) {
|
|
mScrollbar->GetRect(kidRect);
|
|
if (kidRect.Contains(aPoint)) {
|
|
tmp.MoveTo(aPoint.x - kidRect.x, aPoint.y - kidRect.y);
|
|
return mScrollbar->GetFrameForPoint(aPresContext, tmp, aFrame);
|
|
}
|
|
}
|
|
|
|
return nsTableRowGroupFrame::GetFrameForPoint(aPresContext, aPoint, aFrame);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTreeRowGroupFrame::FirstChild(nsIAtom* aListName, nsIFrame** aFirstChild) const
|
|
{
|
|
if (nsXULAtoms::scrollbarlist == aListName) {
|
|
*aFirstChild = mScrollbarList.FirstChild();
|
|
return NS_OK;
|
|
}
|
|
|
|
nsTableRowGroupFrame::FirstChild(aListName, aFirstChild);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTreeRowGroupFrame::GetAdditionalChildListName(PRInt32 aIndex,
|
|
nsIAtom** aListName) const
|
|
{
|
|
*aListName = nsnull;
|
|
|
|
if (aIndex == 0) {
|
|
*aListName = nsXULAtoms::scrollbarlist;
|
|
NS_IF_ADDREF(*aListName);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void nsTreeRowGroupFrame::PaintChildren(nsIPresContext* aPresContext,
|
|
nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aDirtyRect,
|
|
nsFramePaintLayer aWhichLayer)
|
|
{
|
|
nsHTMLContainerFrame::PaintChildren(aPresContext, aRenderingContext, aDirtyRect, aWhichLayer);
|
|
|
|
if (mScrollbar) {
|
|
nsIView *pView;
|
|
|
|
mScrollbar->GetView(aPresContext, &pView);
|
|
if (nsnull == pView) {
|
|
PRBool clipState;
|
|
nsRect kidRect;
|
|
mScrollbar->GetRect(kidRect);
|
|
nsRect damageArea(aDirtyRect);
|
|
// Translate damage area into kid's coordinate system
|
|
nsRect kidDamageArea(damageArea.x - kidRect.x, damageArea.y - kidRect.y,
|
|
damageArea.width, damageArea.height);
|
|
aRenderingContext.PushState();
|
|
aRenderingContext.Translate(kidRect.x, kidRect.y);
|
|
mScrollbar->Paint(aPresContext, aRenderingContext, kidDamageArea, aWhichLayer);
|
|
#ifdef DEBUG
|
|
if ((NS_FRAME_PAINT_LAYER_DEBUG == aWhichLayer) &&
|
|
GetShowFrameBorders()) {
|
|
aRenderingContext.SetColor(NS_RGB(255,0,0));
|
|
aRenderingContext.DrawRect(0, 0, kidRect.width, kidRect.height);
|
|
}
|
|
#endif
|
|
aRenderingContext.PopState(clipState);
|
|
}
|
|
}
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTreeRowGroupFrame::IR_TargetIsChild(nsIPresContext* aPresContext,
|
|
nsHTMLReflowMetrics& aDesiredSize,
|
|
RowGroupReflowState& aReflowState,
|
|
nsReflowStatus& aStatus,
|
|
nsIFrame * aNextFrame)
|
|
{
|
|
if (aNextFrame && (aNextFrame == mScrollbar)) {
|
|
nsresult rv;
|
|
// Recover the state as if aNextFrame is about to be reflowed
|
|
RecoverState(aReflowState, aNextFrame, aDesiredSize.maxElementSize);
|
|
|
|
// Remember the old rect
|
|
nsRect oldKidRect;
|
|
aNextFrame->GetRect(oldKidRect);
|
|
|
|
// Pass along the reflow command
|
|
nsHTMLReflowState kidReflowState(aPresContext, aReflowState.reflowState,
|
|
aNextFrame, aReflowState.availSize);
|
|
kidReflowState.mComputedHeight = mRowGroupHeight;
|
|
nsHTMLReflowMetrics desiredSize(nsnull);
|
|
|
|
rv = ReflowChild(aNextFrame, aPresContext, desiredSize, kidReflowState,
|
|
0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus);
|
|
|
|
nscoord xpos = 0;
|
|
|
|
// Lose the width of the scrollbar as far as the rows are concerned.
|
|
if (aReflowState.availSize.width != NS_UNCONSTRAINEDSIZE) {
|
|
xpos = aReflowState.availSize.width - desiredSize.width;
|
|
/*aReflowState.availSize.width -= desiredSize.width;
|
|
if (aReflowState.availSize.width < 0)
|
|
aReflowState.availSize.width = 0;*/
|
|
}
|
|
|
|
// Place the child
|
|
FinishReflowChild(aNextFrame, aPresContext, desiredSize, xpos, 0, 0);
|
|
|
|
// Return our desired width
|
|
aDesiredSize.width = aReflowState.reflowState.availableWidth;
|
|
|
|
if (mNextInFlow) {
|
|
aStatus = NS_FRAME_NOT_COMPLETE;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
return nsTableRowGroupFrame::IR_TargetIsChild(aPresContext,
|
|
aDesiredSize,
|
|
aReflowState,
|
|
aStatus,
|
|
aNextFrame);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTreeRowGroupFrame::ReflowBeforeRowLayout(nsIPresContext* aPresContext,
|
|
nsHTMLReflowMetrics& aDesiredSize,
|
|
RowGroupReflowState& aReflowState,
|
|
nsReflowStatus& aStatus,
|
|
nsReflowReason aReason)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
mRowGroupHeight = aReflowState.availSize.height;
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTreeRowGroupFrame::ReflowAfterRowLayout(nsIPresContext* aPresContext,
|
|
nsHTMLReflowMetrics& aDesiredSize,
|
|
RowGroupReflowState& aReflowState,
|
|
nsReflowStatus& aStatus,
|
|
nsReflowReason aReason)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
|
|
mRowCount = 0;
|
|
ComputeTotalRowCount(mRowCount, mContent); // XXX This sucks! Needs to be cheap!
|
|
// Our page size is the # of rows instantiated.
|
|
PRInt32 pageRowCount;
|
|
GetRowCount(pageRowCount);
|
|
|
|
if (mScrollbar) {
|
|
PRBool nukeScrollbar=PR_FALSE;
|
|
nsAutoString value;
|
|
|
|
nsCOMPtr<nsIContent> scrollbarContent;
|
|
mScrollbar->GetContent(getter_AddRefs(scrollbarContent));
|
|
|
|
if (mRowCount <= pageRowCount) {
|
|
// first set the position to 0 so that all visible content
|
|
// scrolls into view
|
|
value.Append(0);
|
|
scrollbarContent->SetAttribute(kNameSpaceID_None,
|
|
nsXULAtoms::curpos,
|
|
value, PR_TRUE);
|
|
// now force nuking the scrollbar
|
|
// (otherwise it takes a bunch of reflows to actually make it go away)
|
|
nukeScrollbar=PR_TRUE;
|
|
}
|
|
|
|
else {
|
|
scrollbarContent->GetAttribute(kNameSpaceID_None,
|
|
nsXULAtoms::curpos, value);
|
|
}
|
|
|
|
if (nukeScrollbar || (value == "0" && !mIsFull)) {
|
|
// Nuke the scrollbar.
|
|
mFrameConstructor->RemoveMappingsForFrameSubtree(aPresContext, mScrollbar, nsnull);
|
|
mScrollbarList.DestroyFrames(aPresContext);
|
|
mScrollbar = nsnull;
|
|
}
|
|
}
|
|
|
|
if (mShouldHaveScrollbar && (mRowGroupHeight != NS_UNCONSTRAINEDSIZE) &&
|
|
(mIsFull || mScrollbar)) {
|
|
// Ensure the scrollbar has been created.
|
|
if (!mScrollbar)
|
|
CreateScrollbar(aPresContext);
|
|
|
|
// Set the maxpos of the scrollbar.
|
|
nsCOMPtr<nsIContent> scrollbarContent;
|
|
mScrollbar->GetContent(getter_AddRefs(scrollbarContent));
|
|
|
|
PRInt32 rowCount = mRowCount-1;
|
|
if (rowCount < 0)
|
|
rowCount = 0;
|
|
|
|
nsAutoString maxpos;
|
|
if (!mIsFull) {
|
|
// We are not full. This means that we are not allowed to scroll any further. We are
|
|
// at the max position right now.
|
|
scrollbarContent->GetAttribute(kNameSpaceID_None, nsXULAtoms::curpos, maxpos);
|
|
}
|
|
else {
|
|
|
|
if (pageRowCount < 2)
|
|
pageRowCount = 2;
|
|
|
|
rowCount -= (pageRowCount-2);
|
|
|
|
char ch[100];
|
|
sprintf(ch,"%d", rowCount);
|
|
maxpos = ch;
|
|
}
|
|
|
|
// Make sure our position is accurate.
|
|
scrollbarContent->SetAttribute(kNameSpaceID_None, nsXULAtoms::maxpos, maxpos, PR_FALSE);
|
|
|
|
// We must be constrained, or a scrollbar makes no sense.
|
|
nsSize kidMaxElementSize;
|
|
nsSize* pKidMaxElementSize = (nsnull != aDesiredSize.maxElementSize) ? &kidMaxElementSize : nsnull;
|
|
|
|
nsSize kidAvailSize(aReflowState.availSize);
|
|
nsHTMLReflowMetrics desiredSize(pKidMaxElementSize);
|
|
desiredSize.width=desiredSize.height=desiredSize.ascent=desiredSize.descent=0;
|
|
|
|
// Reflow the child into the available space, giving it as much width as it
|
|
// wants, but constraining its height.
|
|
kidAvailSize.width = NS_UNCONSTRAINEDSIZE;
|
|
nsHTMLReflowState kidReflowState(aPresContext, aReflowState.reflowState, mScrollbar,
|
|
kidAvailSize, aReason);
|
|
|
|
kidReflowState.mComputedHeight = mRowGroupHeight;
|
|
rv = ReflowChild(mScrollbar, aPresContext, desiredSize, kidReflowState,
|
|
0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
nscoord xpos = 0;
|
|
|
|
// Lose the width of the scrollbar as far as the rows are concerned.
|
|
if (aReflowState.availSize.width != NS_UNCONSTRAINEDSIZE) {
|
|
xpos = aReflowState.availSize.width - desiredSize.width;
|
|
/*aReflowState.availSize.width -= desiredSize.width;
|
|
if (aReflowState.availSize.width < 0)
|
|
aReflowState.availSize.width = 0;*/
|
|
}
|
|
|
|
// Place the child
|
|
FinishReflowChild(mScrollbar, aPresContext, desiredSize, xpos, 0, 0);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
|
|
void nsTreeRowGroupFrame::LocateFrame(nsIFrame* aStartFrame, nsIFrame** aResult)
|
|
{
|
|
if (aStartFrame == nsnull)
|
|
{
|
|
*aResult = mFrames.FirstChild();
|
|
}
|
|
else aStartFrame->GetNextSibling(aResult);
|
|
}
|
|
|
|
nsIFrame*
|
|
nsTreeRowGroupFrame::GetFirstFrame()
|
|
{
|
|
// We may just be a normal row group.
|
|
if (!mIsLazy)
|
|
return mFrames.FirstChild();
|
|
|
|
LocateFrame(nsnull, &mTopFrame);
|
|
return mTopFrame;
|
|
}
|
|
|
|
nsIFrame*
|
|
nsTreeRowGroupFrame::GetLastFrame()
|
|
{
|
|
// For now just return the one on the end.
|
|
return mFrames.LastChild();
|
|
}
|
|
|
|
void
|
|
nsTreeRowGroupFrame::GetNextFrame(nsIFrame* aPrevFrame, nsIFrame** aResult)
|
|
{
|
|
if (!mIsLazy)
|
|
aPrevFrame->GetNextSibling(aResult);
|
|
else LocateFrame(aPrevFrame, aResult);
|
|
}
|
|
|
|
nsIFrame*
|
|
nsTreeRowGroupFrame::GetFirstFrameForReflow(nsIPresContext* aPresContext)
|
|
{
|
|
// Clear ourselves out.
|
|
mLinkupFrame = nsnull;
|
|
mBottomFrame = mTopFrame;
|
|
mIsFull = PR_FALSE;
|
|
|
|
// We may just be a normal row group.
|
|
if (!mIsLazy)
|
|
{
|
|
return mFrames.FirstChild();
|
|
}
|
|
|
|
// If we have a frame and no content chain (e.g., unresolved/uncreated content)
|
|
if (mTopFrame && !mContentChain)
|
|
{
|
|
return mTopFrame;
|
|
}
|
|
|
|
// See if we have any frame whatsoever.
|
|
LocateFrame(nsnull, &mTopFrame);
|
|
|
|
mBottomFrame = mTopFrame;
|
|
|
|
nsCOMPtr<nsIContent> startContent;
|
|
if (mContentChain) {
|
|
nsCOMPtr<nsISupports> supports;
|
|
mContentChain->GetElementAt(0, getter_AddRefs(supports));
|
|
nsCOMPtr<nsIContent> chainContent = do_QueryInterface(supports);
|
|
startContent = chainContent;
|
|
|
|
if (mTopFrame) {
|
|
|
|
// We have a content chain. If the top frame is the same as our content
|
|
// chain, we can go ahead and destroy our content chain and return the
|
|
// top frame.
|
|
nsCOMPtr<nsIContent> topContent;
|
|
mTopFrame->GetContent(getter_AddRefs(topContent));
|
|
if (chainContent.get() == topContent.get()) {
|
|
// The two content nodes are the same. Our content chain has
|
|
// been synched up, and we can now remove our element and
|
|
// pass the content chain inwards.
|
|
InitSubContentChain((nsTreeRowGroupFrame*)mTopFrame);
|
|
|
|
return mTopFrame;
|
|
}
|
|
else mLinkupFrame = mTopFrame; // We have some frames that we'll eventually catch up with.
|
|
// Cache the pointer to the first of these frames, so
|
|
// we'll know it when we hit it.
|
|
}
|
|
}
|
|
else if (mTopFrame) {
|
|
return mTopFrame;
|
|
}
|
|
|
|
// We don't have a top frame instantiated. Let's
|
|
// try to make one.
|
|
|
|
// If startContent is initialized, we have a content chain, and
|
|
// we're using that content node to make our frame.
|
|
// Otherwise we have nothing, and we should just try to grab the first child.
|
|
if (!startContent) {
|
|
PRInt32 childCount;
|
|
mContent->ChildCount(childCount);
|
|
nsCOMPtr<nsIContent> childContent;
|
|
if (childCount > 0) {
|
|
mContent->ChildAt(0, *getter_AddRefs(childContent));
|
|
startContent = childContent;
|
|
}
|
|
}
|
|
|
|
if (startContent) {
|
|
nsTableFrame* tableFrame;
|
|
nsTableFrame::GetTableFrame(this, tableFrame);
|
|
|
|
PRBool isAppend = (mLinkupFrame == nsnull);
|
|
|
|
mFrameConstructor->CreateTreeWidgetContent(aPresContext, this, nsnull, startContent,
|
|
&mTopFrame, isAppend, PR_FALSE,
|
|
GetStateStorageObject(this, tableFrame));
|
|
|
|
// XXX Can be optimized if we detect that we're appending a row.
|
|
// Also the act of appending or inserting a row group is harmless.
|
|
|
|
//printf("Created a frame\n");
|
|
mBottomFrame = mTopFrame;
|
|
if (IsTableRowFrame(mTopFrame)) {
|
|
nsCOMPtr<nsIContent> rowContent;
|
|
mTopFrame->GetContent(getter_AddRefs(rowContent));
|
|
|
|
nsCOMPtr<nsIContent> topRowContent;
|
|
PRInt32 delta = 1;
|
|
|
|
FindPreviousRowContent(delta, rowContent, nsnull, getter_AddRefs(topRowContent));
|
|
|
|
AddRowToMap(tableFrame, aPresContext, topRowContent, mTopFrame);
|
|
|
|
PostAppendRow(mTopFrame, aPresContext);
|
|
}
|
|
else if (IsTableRowGroupFrame(mTopFrame) && mContentChain) {
|
|
// We have just instantiated a row group, and we have a content chain. This
|
|
// means we need to potentially pass a sub-content chain to the instantiated
|
|
// frame, so that it can also sync up with its children.
|
|
InitSubContentChain((nsTreeRowGroupFrame*)mTopFrame);
|
|
}
|
|
|
|
SetContentChain(nsnull);
|
|
|
|
return mTopFrame;
|
|
}
|
|
return nsnull;
|
|
}
|
|
|
|
void
|
|
nsTreeRowGroupFrame::GetNextFrameForReflow(nsIPresContext* aPresContext, nsIFrame* aFrame, nsIFrame** aResult)
|
|
{
|
|
|
|
if (mIsLazy) {
|
|
// We're ultra-cool. We build our frames on the fly.
|
|
LocateFrame(aFrame, aResult);
|
|
if (*aResult && (*aResult == mLinkupFrame)) {
|
|
// We haven't really found a result. We've only found a result if
|
|
// the linkup frame is really the next frame following the
|
|
// previous frame.
|
|
nsCOMPtr<nsIContent> prevContent;
|
|
aFrame->GetContent(getter_AddRefs(prevContent));
|
|
nsCOMPtr<nsIContent> linkupContent;
|
|
mLinkupFrame->GetContent(getter_AddRefs(linkupContent));
|
|
PRInt32 i, j;
|
|
mContent->IndexOf(prevContent, i);
|
|
mContent->IndexOf(linkupContent, j);
|
|
if (i+1==j) {
|
|
// We have found a match and successfully linked back up with our
|
|
// old frame.
|
|
mBottomFrame = mLinkupFrame;
|
|
mLinkupFrame = nsnull;
|
|
return;
|
|
}
|
|
else *aResult = nsnull; // No true linkup. We need to make a frame.
|
|
}
|
|
|
|
if (!*aResult) {
|
|
// No result found. See if there's a content node that wants a frame.
|
|
PRInt32 i, childCount;
|
|
nsCOMPtr<nsIContent> prevContent;
|
|
aFrame->GetContent(getter_AddRefs(prevContent));
|
|
nsCOMPtr<nsIContent> parentContent;
|
|
mContent->IndexOf(prevContent, i);
|
|
mContent->ChildCount(childCount);
|
|
if (i+1 < childCount) {
|
|
nsTableFrame* tableFrame;
|
|
nsTableFrame::GetTableFrame(this, tableFrame);
|
|
|
|
// There is a content node that wants a frame.
|
|
nsCOMPtr<nsIContent> nextContent;
|
|
mContent->ChildAt(i+1, *getter_AddRefs(nextContent));
|
|
nsIFrame* prevFrame = nsnull; // Default is to append
|
|
PRBool isAppend = PR_TRUE;
|
|
if (mLinkupFrame) {
|
|
// This will be an insertion, since we have frames on the end.
|
|
prevFrame = aFrame;
|
|
isAppend = PR_FALSE;
|
|
}
|
|
mFrameConstructor->CreateTreeWidgetContent(aPresContext, this, prevFrame, nextContent,
|
|
aResult, isAppend, PR_FALSE,
|
|
GetStateStorageObject(this, tableFrame));
|
|
|
|
// XXX Can be optimized if we detect that we're appending a row to the end of the tree.
|
|
// Also the act of appending or inserting a row group is harmless.
|
|
if (IsTableRowFrame(*aResult)) {
|
|
|
|
nsCOMPtr<nsIContent> topRowContent;
|
|
PRInt32 delta = 1;
|
|
FindPreviousRowContent(delta, nextContent, nsnull, getter_AddRefs(topRowContent));
|
|
AddRowToMap(tableFrame, aPresContext, topRowContent, (*aResult));
|
|
PostAppendRow(*aResult, aPresContext);
|
|
}
|
|
}
|
|
}
|
|
|
|
mBottomFrame = *aResult;
|
|
return;
|
|
}
|
|
|
|
// Ho-hum. Move along, nothing to see here.
|
|
aFrame->GetNextSibling(aResult);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTreeRowGroupFrame::TreeInsertFrames(nsIFrame* aPrevFrame, nsIFrame* aFrameList)
|
|
{
|
|
mFrames.InsertFrames(nsnull, aPrevFrame, aFrameList);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTreeRowGroupFrame::TreeAppendFrames(nsIFrame* aFrameList)
|
|
{
|
|
mFrames.AppendFrames(nsnull, aFrameList);
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
PRBool nsTreeRowGroupFrame::ContinueReflow(nsIPresContext* aPresContext, nscoord y, nscoord height)
|
|
{
|
|
//printf("Y is: %d\n", y);
|
|
//printf("Height is: %d\n", height);
|
|
if (height <= 0 && IsLazy()) {
|
|
mIsFull = PR_TRUE;
|
|
nsIFrame* lastChild = GetLastFrame();
|
|
nsIFrame* startingPoint = mBottomFrame;
|
|
if (startingPoint == nsnull) {
|
|
// We just want to delete everything but the first item.
|
|
startingPoint = GetFirstFrame();
|
|
}
|
|
|
|
if (lastChild != startingPoint) {
|
|
// We have some hangers on (probably caused by shrinking the size of the window).
|
|
// Nuke them.
|
|
nsIFrame* currFrame;
|
|
startingPoint->GetNextSibling(&currFrame);
|
|
while (currFrame) {
|
|
nsIFrame* nextFrame;
|
|
currFrame->GetNextSibling(&nextFrame);
|
|
mFrameConstructor->RemoveMappingsForFrameSubtree(aPresContext, currFrame, nsnull);
|
|
mFrames.DestroyFrame(aPresContext, currFrame);
|
|
currFrame = nextFrame;
|
|
//printf("Nuked one off the end.\n");
|
|
}
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
else
|
|
return PR_TRUE;
|
|
}
|
|
|
|
// Responses to changes
|
|
void nsTreeRowGroupFrame::OnContentAdded(nsIPresContext* aPresContext)
|
|
{
|
|
nsTableFrame* tableFrame;
|
|
nsTableFrame::GetTableFrame(this, tableFrame);
|
|
|
|
nsTreeFrame* treeFrame = (nsTreeFrame*) tableFrame;
|
|
MarkTreeAsDirty(aPresContext, treeFrame);
|
|
}
|
|
|
|
void nsTreeRowGroupFrame::OnContentInserted(nsIPresContext* aPresContext, nsIFrame* aNextSibling, PRInt32 aIndex)
|
|
{
|
|
nsIFrame* currFrame = aNextSibling;
|
|
|
|
// this will probably never happen -
|
|
// if we haven't created the topframe, it doesn't matter if
|
|
// content was inserted
|
|
if (mTopFrame == nsnull) return;
|
|
|
|
// if we're inserting content at the top of visible content,
|
|
// then ignore it because it would go off-screen
|
|
// except of course in the case of the first row, where we're
|
|
// actually adding visible content
|
|
if(aNextSibling == mTopFrame) {
|
|
if (aIndex == 0)
|
|
// it's the first row, blow away mTopFrame so it can be
|
|
// crecreated later
|
|
mTopFrame = nsnull;
|
|
else
|
|
// it's not visible, nothing to do
|
|
return;
|
|
}
|
|
|
|
while (currFrame) {
|
|
nsIFrame* nextFrame;
|
|
currFrame->GetNextSibling(&nextFrame);
|
|
mFrameConstructor->RemoveMappingsForFrameSubtree(aPresContext, currFrame,
|
|
GetStateStorageObject(this, NULL));
|
|
mFrames.DestroyFrame(aPresContext, currFrame);
|
|
currFrame = nextFrame;
|
|
//printf("Nuked one off the end.\n");
|
|
}
|
|
OnContentAdded(aPresContext);
|
|
|
|
}
|
|
|
|
void nsTreeRowGroupFrame::OnContentRemoved(nsIPresContext* aPresContext,
|
|
nsIFrame* aChildFrame)
|
|
{
|
|
|
|
// if we're removing the top row, the new top row is the next row
|
|
if (mTopFrame == aChildFrame)
|
|
mTopFrame->GetNextSibling(&mTopFrame);
|
|
|
|
nsTableFrame* tableFrame;
|
|
nsTableFrame::GetTableFrame(this, tableFrame);
|
|
|
|
nsTreeFrame* treeFrame = (nsTreeFrame*)tableFrame;
|
|
|
|
// Go ahead and delete the frame.
|
|
if (aChildFrame) {
|
|
mFrameConstructor->RemoveMappingsForFrameSubtree(aPresContext, aChildFrame, nsnull);
|
|
mFrames.DestroyFrame(aPresContext, aChildFrame);
|
|
treeFrame->InvalidateCellMap();
|
|
treeFrame->InvalidateColumnCache();
|
|
}
|
|
|
|
MarkTreeAsDirty(aPresContext, treeFrame);
|
|
}
|
|
|
|
void nsTreeRowGroupFrame::SetContentChain(nsISupportsArray* aContentChain)
|
|
{
|
|
NS_IF_RELEASE(mContentChain);
|
|
mContentChain = aContentChain;
|
|
NS_IF_ADDREF(mContentChain);
|
|
}
|
|
|
|
void nsTreeRowGroupFrame::InitSubContentChain(nsTreeRowGroupFrame* aRowGroupFrame)
|
|
{
|
|
if (mContentChain) {
|
|
mContentChain->RemoveElementAt(0);
|
|
PRUint32 chainSize;
|
|
mContentChain->Count(&chainSize);
|
|
if (chainSize > 0 && aRowGroupFrame) {
|
|
aRowGroupFrame->SetContentChain(mContentChain);
|
|
}
|
|
// The chain is dead. Long live the chain.
|
|
SetContentChain(nsnull);
|
|
}
|
|
}
|
|
|
|
void nsTreeRowGroupFrame::SetShouldHaveScrollbar()
|
|
{
|
|
mShouldHaveScrollbar = PR_TRUE;
|
|
mIsLazy = PR_TRUE;
|
|
}
|
|
|
|
void nsTreeRowGroupFrame::CreateScrollbar(nsIPresContext* aPresContext)
|
|
{
|
|
if (mShouldHaveScrollbar && !mScrollbar) {
|
|
// Create an anonymous scrollbar node.
|
|
nsCOMPtr<nsIDocument> idocument;
|
|
mContent->GetDocument(*getter_AddRefs(idocument));
|
|
|
|
nsCOMPtr<nsIDOMDocument> document(do_QueryInterface(idocument));
|
|
|
|
nsCOMPtr<nsIDOMElement> node;
|
|
document->CreateElement("scrollbar",getter_AddRefs(node));
|
|
|
|
nsCOMPtr<nsIContent> content = do_QueryInterface(node);
|
|
content->SetParent(mContent);
|
|
|
|
nsCOMPtr<nsIAtom> align = dont_AddRef(NS_NewAtom("align"));
|
|
content->SetAttribute(kNameSpaceID_None, align, "vertical", PR_FALSE);
|
|
|
|
nsIFrame* aResult;
|
|
mFrameConstructor->CreateTreeWidgetContent(aPresContext, this, nsnull, content,
|
|
&aResult, PR_FALSE, PR_TRUE, nsnull);
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
nsTreeRowGroupFrame::IndexOfCell(nsIPresContext* aPresContext,
|
|
nsIContent* aCellContent, PRInt32& aRowIndex, PRInt32& aColIndex)
|
|
{
|
|
// Get the index of our parent row.
|
|
nsCOMPtr<nsIContent> row;
|
|
aCellContent->GetParent(*getter_AddRefs(row));
|
|
IndexOfRow(aPresContext, row, aRowIndex);
|
|
if (aRowIndex == -1)
|
|
return;
|
|
|
|
// To determine the column index, just ask what our indexOf is.
|
|
row->IndexOf(aCellContent, aColIndex);
|
|
}
|
|
|
|
// returns the 0-based index of the content node, within the content tree
|
|
void
|
|
nsTreeRowGroupFrame::IndexOfRow(nsIPresContext* aPresContext,
|
|
nsIContent* aRowContent, PRInt32& aRowIndex)
|
|
{
|
|
// Use GetPrimaryFrameFor to retrieve the frame.
|
|
// This crawls only the frame tree, and will be much faster for the case
|
|
// where the frame is onscreen.
|
|
nsCOMPtr<nsIPresShell> shell;
|
|
aPresContext->GetShell(getter_AddRefs(shell));
|
|
|
|
nsIFrame* result = nsnull;
|
|
shell->GetPrimaryFrameFor(aRowContent, &result);
|
|
|
|
if (result) {
|
|
// We found a frame. This is good news. It means we can look at our row
|
|
// index and just adjust based on our current offset index.
|
|
nsTableRowFrame* row = (nsTableRowFrame*)result;
|
|
PRInt32 screenRowIndex = row->GetRowIndex();
|
|
aRowIndex = screenRowIndex + mCurrentIndex;
|
|
}
|
|
else {
|
|
// We didn't find a frame. This mean we have no choice but to crawl
|
|
// the row group.
|
|
#ifdef DEBUG_tree
|
|
printf("Searching for non-visible content node\n");
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
PRBool
|
|
nsTreeRowGroupFrame::IsValidRow(PRInt32 aRowIndex)
|
|
{
|
|
if (aRowIndex >= 0 && aRowIndex < mRowCount)
|
|
return PR_TRUE;
|
|
return PR_FALSE;
|
|
}
|
|
|
|
void
|
|
nsTreeRowGroupFrame::EnsureRowIsVisible(PRInt32 aRowIndex)
|
|
{
|
|
// if no scrollbar, then it must be visible
|
|
if (!mScrollbar) return;
|
|
|
|
PRInt32 rows;
|
|
GetRowCount(rows);
|
|
PRInt32 bottomIndex = mCurrentIndex + rows - 1;
|
|
|
|
// if row is visible, ignore
|
|
if (mCurrentIndex <= aRowIndex && aRowIndex <= bottomIndex)
|
|
return;
|
|
|
|
#ifdef DEBUG_tree
|
|
printf("top = %d, bottom = %d, going to %d\n",
|
|
mCurrentIndex, bottomIndex, aRowIndex);
|
|
#endif
|
|
|
|
nsCOMPtr<nsIContent> scrollbarContent;
|
|
mScrollbar->GetContent(getter_AddRefs(scrollbarContent));
|
|
|
|
PRInt32 scrollTo = mCurrentIndex;
|
|
if (aRowIndex < mCurrentIndex) {
|
|
// row is above us, scroll up from mCurrentIndex
|
|
// scroll such that the top row is aRowIndex
|
|
#ifdef DEBUG_tree
|
|
printf("row is above, scroll to %d\n", aRowIndex);
|
|
#endif
|
|
scrollTo=aRowIndex;
|
|
} else {
|
|
// aRowIndex > bottomIndex here
|
|
// row is below us, so scroll down from bottomIndex
|
|
// scroll such that the top row is "rows" above aRowIndex
|
|
NS_ASSERTION(aRowIndex - rows >=0, "scrolling to negative row?!");
|
|
scrollTo=aRowIndex - rows + 1;
|
|
#ifdef DEBUG_tree
|
|
printf("row is below, scroll to %d\n", scrollTo);
|
|
#endif
|
|
|
|
}
|
|
|
|
nsAutoString value;
|
|
|
|
#ifdef DEBUG_tree
|
|
// dump state
|
|
scrollbarContent->GetAttribute(kNameSpaceID_None, nsXULAtoms::maxpos, value);
|
|
printf("maxpos = %s\n", value.ToNewCString());
|
|
scrollbarContent->GetAttribute(kNameSpaceID_None, nsXULAtoms::curpos, value);
|
|
printf("curpos = %s\n", value.ToNewCString());
|
|
|
|
value="";
|
|
#endif
|
|
|
|
value.Append(scrollTo);
|
|
scrollbarContent->SetAttribute(kNameSpaceID_None, nsXULAtoms::curpos,
|
|
value, PR_TRUE);
|
|
}
|
|
|
|
void
|
|
nsTreeRowGroupFrame::GetCellFrameAtIndex(PRInt32 aRowIndex, PRInt32 aColIndex,
|
|
nsTreeCellFrame** aResult)
|
|
{
|
|
#ifdef DEBUG_tree
|
|
printf("Looking for cell (%d, %d)..", aRowIndex, aColIndex);
|
|
#endif
|
|
// The screen index = (aRowIndex - mCurrentIndex)
|
|
PRInt32 screenIndex = aRowIndex - mCurrentIndex;
|
|
|
|
// Get the table frame.
|
|
nsTableFrame* tableFrame;
|
|
nsTableFrame::GetTableFrame(this, tableFrame);
|
|
|
|
nsTableCellFrame* cellFrame;
|
|
|
|
#ifdef DEBUG_tree
|
|
printf("(screen index (%d,%d))\n", screenIndex, aColIndex);
|
|
#endif
|
|
|
|
nsCellMap * cellMap = tableFrame->GetCellMap();
|
|
CellData* cellData = cellMap->GetCellAt(screenIndex, aColIndex);
|
|
if (cellData) {
|
|
cellFrame = cellData->mOrigCell;
|
|
if (cellFrame) { // the cell originates at (rowX, colX)
|
|
*aResult = (nsTreeCellFrame*)cellFrame; // XXX I am evil.
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG_tree
|
|
printf("got cell frame %p\n", *aResult);
|
|
#endif
|
|
}
|
|
|
|
void nsTreeRowGroupFrame::PostAppendRow(nsIFrame* aRowFrame, nsIPresContext* aPresContext)
|
|
{
|
|
// shouldn't need this anymore
|
|
// DidAppendRow((nsTableRowFrame*)aRowFrame);
|
|
|
|
// Get the table frame.
|
|
nsTableFrame* tableFrame;
|
|
nsTableFrame::GetTableFrame(this, tableFrame);
|
|
|
|
// See if any implicit column frames need to be created as a result of
|
|
// adding the new rows
|
|
PRBool createdColFrames;
|
|
tableFrame->EnsureColumns(aPresContext, createdColFrames);
|
|
if (createdColFrames) {
|
|
// We need to rebuild the column cache
|
|
// XXX It would be nice if this could be done incrementally
|
|
tableFrame->InvalidateColumnCache();
|
|
|
|
MarkTreeAsDirty(aPresContext, (nsTreeFrame*) tableFrame);
|
|
}
|
|
}
|
|
|
|
void nsTreeRowGroupFrame::ScrollByLines(nsIPresContext* aPresContext,
|
|
PRInt32 lines)
|
|
{
|
|
if (nsnull == mScrollbar) // nothing to scroll
|
|
return;
|
|
|
|
PRInt32 scrollTo = mCurrentIndex + lines;
|
|
PRInt32 visRows, totalRows;
|
|
totalRows = GetVisibleRowCount();
|
|
GetRowCount(visRows);
|
|
|
|
if (scrollTo < 0)
|
|
scrollTo = 0;
|
|
if (!IsValidRow(scrollTo + visRows - 1)) // don't go down too far
|
|
scrollTo = totalRows - visRows + 1;
|
|
nsAutoString value;
|
|
value.Append(scrollTo);
|
|
|
|
nsCOMPtr<nsIContent> scrollbarContent;
|
|
mScrollbar->GetContent(getter_AddRefs(scrollbarContent));
|
|
if (scrollbarContent)
|
|
scrollbarContent->SetAttribute(kNameSpaceID_None, nsXULAtoms::curpos,
|
|
value, PR_TRUE);
|
|
}
|
|
|
|
|
|
void
|
|
nsTreeRowGroupFrame::MarkTreeAsDirty(nsIPresContext* aPresContext, nsTreeFrame* aTreeFrame)
|
|
{
|
|
if (IsLazy() && !aTreeFrame->IsSlatedForReflow()) {
|
|
aTreeFrame->SlateForReflow();
|
|
|
|
// Mark the table frame as dirty
|
|
nsFrameState frameState;
|
|
|
|
aTreeFrame->GetFrameState(&frameState);
|
|
frameState |= NS_FRAME_IS_DIRTY;
|
|
aTreeFrame->SetFrameState(frameState);
|
|
|
|
// Schedule a reflow for us.
|
|
nsCOMPtr<nsIReflowCommand> reflowCmd;
|
|
nsIFrame* outerTableFrame;
|
|
nsresult rv;
|
|
|
|
aTreeFrame->GetParent(&outerTableFrame);
|
|
rv = NS_NewHTMLReflowCommand(getter_AddRefs(reflowCmd), aTreeFrame,
|
|
nsIReflowCommand::ReflowDirty);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
nsCOMPtr<nsIPresShell> presShell;
|
|
aPresContext->GetShell(getter_AddRefs(presShell));
|
|
presShell->AppendReflowCommand(reflowCmd);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// given a content node, insert a row into the cellmap
|
|
// for wherever it belows in the map
|
|
void
|
|
nsTreeRowGroupFrame::AddRowToMap(nsTableFrame *aTableFrame,
|
|
nsIPresContext* aPresContext,
|
|
nsIContent *aContent,
|
|
nsIFrame* aCurrentFrame)
|
|
{
|
|
PRInt32 insertionIndex =
|
|
GetInsertionIndexForContent(aTableFrame, aPresContext, aContent);
|
|
|
|
aTableFrame->InsertRowIntoMap((nsTableRowFrame*)aCurrentFrame,
|
|
insertionIndex);
|
|
}
|
|
|
|
|
|
// determine the appropriate index of this row in the row map
|
|
PRInt32
|
|
nsTreeRowGroupFrame::GetInsertionIndexForContent(nsTableFrame *aTableFrame,
|
|
nsIPresContext* aPresContext,
|
|
nsIContent *aContent)
|
|
{
|
|
|
|
PRInt32 insertionIndex = -1;
|
|
|
|
// see if there is a frame for aContent
|
|
if (aContent) {
|
|
// Retrieve the primary frame.
|
|
nsCOMPtr<nsIPresShell> shell;
|
|
aPresContext->GetShell(getter_AddRefs(shell));
|
|
|
|
nsIFrame* result = nsnull;
|
|
shell->GetPrimaryFrameFor(aContent, &result);
|
|
if (result) {
|
|
// We have a primary frame. Get its row index. We're equal to that + 1.
|
|
nsTableRowFrame* rowFrame = (nsTableRowFrame*)result;
|
|
insertionIndex = rowFrame->GetRowIndex() + 1;
|
|
}
|
|
}
|
|
|
|
// nope, no frame, so go find where it belongs by walking the frames
|
|
if (insertionIndex==-1)
|
|
insertionIndex = ((nsTreeFrame*)aTableFrame)->GetInsertionIndex(this);
|
|
|
|
return insertionIndex;
|
|
}
|
|
|
|
// get the row index of aFrame
|
|
// recurse into rowgroups, count rows
|
|
PRInt32
|
|
nsTreeRowGroupFrame::GetInsertionIndex(nsIFrame *aFrame, PRInt32 aCurrentIndex, PRBool& aDone)
|
|
{
|
|
nsIFrame *child = mFrames.FirstChild();
|
|
|
|
PRInt32 index=aCurrentIndex;
|
|
while (child) {
|
|
|
|
// stop when we hit aFrame
|
|
if (child==aFrame) {
|
|
aDone=PR_TRUE;
|
|
return index;
|
|
}
|
|
|
|
// recurse into rowgroups
|
|
if (IsTableRowGroupFrame(child)) {
|
|
index = ((nsTreeRowGroupFrame*)child)->GetInsertionIndex(aFrame, index, aDone);
|
|
// short-circut the return
|
|
if (aDone) return index;
|
|
}
|
|
|
|
// count rows
|
|
else if (IsTableRowFrame(child))
|
|
index++;
|
|
|
|
child->GetNextSibling(&child);
|
|
}
|
|
|
|
return index;
|
|
}
|
|
|
|
void
|
|
nsTreeRowGroupFrame::GetFirstRowFrame(nsTableRowFrame **aRowFrame)
|
|
{
|
|
nsIFrame* child = mFrames.FirstChild();
|
|
|
|
while (child) {
|
|
if (IsTableRowFrame(child)) {
|
|
*aRowFrame = (nsTableRowFrame*)child;
|
|
return;
|
|
}
|
|
|
|
if (IsTableRowGroupFrame(child)) {
|
|
((nsTreeRowGroupFrame*)child)->GetFirstRowFrame(aRowFrame);
|
|
if (*aRowFrame) return;
|
|
}
|
|
|
|
child->GetNextSibling(&child);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// pinkerton
|
|
// code copied from the toolbar to bootstrap tree d&d. I hope to god
|
|
// it goes away and i don't forget about it.
|
|
//
|
|
// We really _want_ to use CSS to do the drawing/etc, but changing the
|
|
// borders of the cells would cause reflows and that could really suck.
|
|
// As a first stab, let's try just to paint ourselves.
|
|
//
|
|
|
|
#include "nsIView.h"
|
|
#include "nsIViewManager.h"
|
|
|
|
|
|
//XXX NOTE: try to consolodate with the version in the toolbar code.
|
|
static void ForceDrawFrame(nsIPresContext* aPresContext, nsIFrame * aFrame);
|
|
static void ForceDrawFrame(nsIPresContext* aPresContext, nsIFrame * aFrame)
|
|
{
|
|
if (aFrame == nsnull) {
|
|
return;
|
|
}
|
|
nsRect rect;
|
|
nsIView * view;
|
|
nsPoint pnt;
|
|
aFrame->GetOffsetFromView(aPresContext, pnt, &view);
|
|
aFrame->GetRect(rect);
|
|
rect.x = pnt.x;
|
|
rect.y = pnt.y;
|
|
if (view) {
|
|
nsCOMPtr<nsIViewManager> viewMgr;
|
|
view->GetViewManager(*getter_AddRefs(viewMgr));
|
|
if (viewMgr)
|
|
viewMgr->UpdateView(view, rect, NS_VMREFRESH_AUTO_DOUBLE_BUFFER | NS_VMREFRESH_IMMEDIATE);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// AttributeChanged
|
|
//
|
|
// Track several attributes set by the d&d drop feedback tracking mechanism. The first
|
|
// is the "dd-triggerrepaint" attribute so JS can trigger a repaint when it
|
|
// needs up update the drop feedback. The second is the x (or y, if bar is vertical)
|
|
// coordinate of where the drop feedback bar should be drawn.
|
|
//
|
|
NS_IMETHODIMP
|
|
nsTreeRowGroupFrame :: AttributeChanged ( nsIPresContext* aPresContext, nsIContent* aChild,
|
|
PRInt32 aNameSpaceID, nsIAtom* aAttribute, PRInt32 aHint)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
|
|
if ( aAttribute == nsXULAtoms::ddTriggerRepaint )
|
|
ForceDrawFrame ( aPresContext, this );
|
|
else if ( aAttribute == nsXULAtoms::ddDropLocationCoord ) {
|
|
nsAutoString attribute;
|
|
aChild->GetAttribute ( kNameSpaceID_None, aAttribute, attribute );
|
|
char* iHateNSString = attribute.ToNewCString();
|
|
mYDropLoc = atoi( iHateNSString );
|
|
nsAllocator::Free ( iHateNSString );
|
|
}
|
|
else if ( aAttribute == nsXULAtoms::ddDropOn ) {
|
|
nsAutoString attribute;
|
|
aChild->GetAttribute ( kNameSpaceID_None, aAttribute, attribute );
|
|
attribute.ToLowerCase();
|
|
mDropOnContainer = attribute.Equals("true");
|
|
}
|
|
else
|
|
rv = nsTableRowGroupFrame::AttributeChanged ( aPresContext, aChild, aNameSpaceID, aAttribute, aHint );
|
|
|
|
return rv;
|
|
|
|
} // AttributeChanged
|
|
|
|
|
|
|
|
//
|
|
// Paint
|
|
//
|
|
// Used to draw the drop feedback based on attributes set by the drag event capturer
|
|
//
|
|
NS_IMETHODIMP
|
|
nsTreeRowGroupFrame :: Paint ( nsIPresContext* aPresContext, nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aDirtyRect, nsFramePaintLayer aWhichLayer)
|
|
{
|
|
nsresult res = nsTableRowGroupFrame::Paint ( aPresContext, aRenderingContext, aDirtyRect, aWhichLayer );
|
|
|
|
if ( mYDropLoc != -1 || mDropOnContainer ) {
|
|
// go looking for the psuedo-style that describes the drop feedback marker. If we don't
|
|
// have it yet, go looking for it.
|
|
if (!mMarkerStyle) {
|
|
nsCOMPtr<nsIAtom> atom ( getter_AddRefs(NS_NewAtom(":-moz-drop-marker")) );
|
|
aPresContext->ProbePseudoStyleContextFor(mContent, atom, mStyleContext,
|
|
PR_FALSE, getter_AddRefs(mMarkerStyle));
|
|
}
|
|
|
|
nscolor color;
|
|
if ( mMarkerStyle ) {
|
|
const nsStyleColor* styleColor =
|
|
NS_STATIC_CAST(const nsStyleColor*, mMarkerStyle->GetStyleData(eStyleStruct_Color));
|
|
color = styleColor->mColor;
|
|
}
|
|
else
|
|
color = NS_RGB(0,0,0);
|
|
|
|
// draw different drop feedback depending on if we are dropping on the
|
|
// container or above/below it
|
|
if ( !mDropOnContainer ) {
|
|
|
|
//XXX compute horiz indentation, fix up constants.
|
|
|
|
aRenderingContext.SetColor(color);
|
|
nsRect dividingLine ( 0, mYDropLoc, 20*50, 30 );
|
|
aRenderingContext.FillRect(dividingLine);
|
|
}
|
|
else {
|
|
aRenderingContext.SetColor(NS_RGB(0x7F,0x7F,0x7F));
|
|
nsRect treeItemBounds ( 0, 0, mRect.width, mRect.height );
|
|
aRenderingContext.DrawRect ( treeItemBounds );
|
|
}
|
|
}
|
|
|
|
return res;
|
|
|
|
} // Paint
|
|
|
|
static nsILayoutHistoryState*
|
|
GetStateStorageObject(nsTreeRowGroupFrame* aTreeRowGroupFrame, nsTableFrame* aTableFrame)
|
|
{
|
|
nsILayoutHistoryState* object = nsnull;
|
|
nsTableFrame* tableFrame = nsnull;
|
|
|
|
if (aTableFrame)
|
|
tableFrame = aTableFrame;
|
|
else
|
|
nsTableFrame::GetTableFrame(aTreeRowGroupFrame, tableFrame);
|
|
|
|
if (tableFrame) {
|
|
nsTreeFrame* treeFrame = (nsTreeFrame*) tableFrame;
|
|
treeFrame->GetFrameStateStorageObject(&object);
|
|
}
|
|
return object;
|
|
}
|