diff --git a/content/shared/public/nsXULAtomList.h b/content/shared/public/nsXULAtomList.h index 04fce9b66559..408c1257b44f 100644 --- a/content/shared/public/nsXULAtomList.h +++ b/content/shared/public/nsXULAtomList.h @@ -98,7 +98,10 @@ XUL_ATOM(focus, "focus") XUL_ATOM(outliner, "outliner") XUL_ATOM(outlinerbody, "outlinerbody") XUL_ATOM(outlinercol, "outlinercol") +XUL_ATOM(outlinerchildren, "outlinerchildren") +XUL_ATOM(outlineritem, "outlineritem") XUL_ATOM(outlinerrow, "outlinerrow") +XUL_ATOM(outlinerseparator, "outlinerseparator") XUL_ATOM(outlinercell, "outlinercell") XUL_ATOM(cycler, "cycler") diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index 198a85a2d772..6b5750008288 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -7790,6 +7790,14 @@ nsCSSFrameConstructor::ContentAppended(nsIPresContext* aPresContext, PRInt32 namespaceID; bindingManager->ResolveTag(aContainer, &namespaceID, getter_AddRefs(tag)); + // Just ignore outliner tags, anyway we don't create any frames for them. + if (tag == nsXULAtoms::outlineritem || + tag == nsXULAtoms::outlinerseparator || + tag == nsXULAtoms::outlinerchildren || + tag == nsXULAtoms::outlinerrow || + tag == nsXULAtoms::outlinercell) + return NS_OK; + PRBool treeChildren = tag.get() == nsXULAtoms::treechildren; PRBool treeItem = tag.get() == nsXULAtoms::treeitem; @@ -8370,6 +8378,14 @@ nsCSSFrameConstructor::ContentInserted(nsIPresContext* aPresContext, PRInt32 namespaceID; bindingManager->ResolveTag(aContainer, &namespaceID, getter_AddRefs(tag)); + // Just ignore outliner tags, anyway we don't create any frames for them. + if (tag == nsXULAtoms::outlineritem || + tag == nsXULAtoms::outlinerseparator || + tag == nsXULAtoms::outlinerchildren || + tag == nsXULAtoms::outlinerrow || + tag == nsXULAtoms::outlinercell) + return NS_OK; + PRBool treeChildren = tag && tag.get() == nsXULAtoms::treechildren; PRBool treeItem = tag && tag.get() == nsXULAtoms::treeitem; if (treeChildren || treeItem) { @@ -9228,6 +9244,14 @@ nsCSSFrameConstructor::ContentRemoved(nsIPresContext* aPresContext, PRInt32 namespaceID; bindingManager->ResolveTag(aContainer, &namespaceID, getter_AddRefs(tag)); + // Just ignore outliner tags, anyway we don't create any frames for them. + if (tag == nsXULAtoms::outlineritem || + tag == nsXULAtoms::outlinerseparator || + tag == nsXULAtoms::outlinerchildren || + tag == nsXULAtoms::outlinerrow || + tag == nsXULAtoms::outlinercell) + return NS_OK; + PRBool treeChildren = tag && tag.get() == nsXULAtoms::treechildren; PRBool treeItem = tag && tag.get() == nsXULAtoms::treeitem; diff --git a/layout/html/style/src/nsCSSFrameConstructor.cpp b/layout/html/style/src/nsCSSFrameConstructor.cpp index 198a85a2d772..6b5750008288 100644 --- a/layout/html/style/src/nsCSSFrameConstructor.cpp +++ b/layout/html/style/src/nsCSSFrameConstructor.cpp @@ -7790,6 +7790,14 @@ nsCSSFrameConstructor::ContentAppended(nsIPresContext* aPresContext, PRInt32 namespaceID; bindingManager->ResolveTag(aContainer, &namespaceID, getter_AddRefs(tag)); + // Just ignore outliner tags, anyway we don't create any frames for them. + if (tag == nsXULAtoms::outlineritem || + tag == nsXULAtoms::outlinerseparator || + tag == nsXULAtoms::outlinerchildren || + tag == nsXULAtoms::outlinerrow || + tag == nsXULAtoms::outlinercell) + return NS_OK; + PRBool treeChildren = tag.get() == nsXULAtoms::treechildren; PRBool treeItem = tag.get() == nsXULAtoms::treeitem; @@ -8370,6 +8378,14 @@ nsCSSFrameConstructor::ContentInserted(nsIPresContext* aPresContext, PRInt32 namespaceID; bindingManager->ResolveTag(aContainer, &namespaceID, getter_AddRefs(tag)); + // Just ignore outliner tags, anyway we don't create any frames for them. + if (tag == nsXULAtoms::outlineritem || + tag == nsXULAtoms::outlinerseparator || + tag == nsXULAtoms::outlinerchildren || + tag == nsXULAtoms::outlinerrow || + tag == nsXULAtoms::outlinercell) + return NS_OK; + PRBool treeChildren = tag && tag.get() == nsXULAtoms::treechildren; PRBool treeItem = tag && tag.get() == nsXULAtoms::treeitem; if (treeChildren || treeItem) { @@ -9228,6 +9244,14 @@ nsCSSFrameConstructor::ContentRemoved(nsIPresContext* aPresContext, PRInt32 namespaceID; bindingManager->ResolveTag(aContainer, &namespaceID, getter_AddRefs(tag)); + // Just ignore outliner tags, anyway we don't create any frames for them. + if (tag == nsXULAtoms::outlineritem || + tag == nsXULAtoms::outlinerseparator || + tag == nsXULAtoms::outlinerchildren || + tag == nsXULAtoms::outlinerrow || + tag == nsXULAtoms::outlinercell) + return NS_OK; + PRBool treeChildren = tag && tag.get() == nsXULAtoms::treechildren; PRBool treeItem = tag && tag.get() == nsXULAtoms::treeitem; diff --git a/layout/xul/base/src/outliner/public/Makefile.in b/layout/xul/base/src/outliner/public/Makefile.in index 46a11d529734..b9f73da186e4 100644 --- a/layout/xul/base/src/outliner/public/Makefile.in +++ b/layout/xul/base/src/outliner/public/Makefile.in @@ -33,6 +33,7 @@ XPIDLSRCS= nsIOutlinerView.idl \ nsIOutlinerSelection.idl \ nsIOutlinerBoxObject.idl \ nsIOutlinerColFrame.idl \ + nsIOutlinerContentView.idl \ $(NULL) EXPORTS := $(addprefix $(srcdir)/, $(EXPORTS)) diff --git a/layout/xul/base/src/outliner/public/makefile.win b/layout/xul/base/src/outliner/public/makefile.win index a68adc6af884..be9e47b581ac 100644 --- a/layout/xul/base/src/outliner/public/makefile.win +++ b/layout/xul/base/src/outliner/public/makefile.win @@ -25,6 +25,7 @@ XPIDLSRCS= .\nsIOutlinerView.idl \ .\nsIOutlinerSelection.idl \ .\nsIOutlinerBoxObject.idl \ .\nsIOutlinerColFrame.idl \ + .\nsIOutlinerContentView.idl \ $(NULL) MODULE=layout_xul diff --git a/layout/xul/base/src/outliner/public/nsIOutlinerContentView.idl b/layout/xul/base/src/outliner/public/nsIOutlinerContentView.idl new file mode 100644 index 000000000000..8b5a9143c19b --- /dev/null +++ b/layout/xul/base/src/outliner/public/nsIOutlinerContentView.idl @@ -0,0 +1,59 @@ +/* -*- 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.org 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): + * Jan Varga (varga@utcru.sk) + * + * 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 "nsISupports.idl" +#include "nsIDOMElement.idl" + +[scriptable, uuid(F066F69A-6FFE-4117-A85C-ABE9D15C6BBC)] +interface nsIOutlinerContentView : nsISupports +{ + /** + * The element in the DOM which this view uses as root content. + */ + attribute nsIDOMElement root; + + /** + * Retrieve the content item associated with the specified index. + */ + nsIDOMElement getItemAtIndex(in long index); + + /** + * Retrieve the index associated with the specified content item. + */ + long getIndexOfItem(in nsIDOMElement item); +}; diff --git a/layout/xul/base/src/outliner/src/Makefile.in b/layout/xul/base/src/outliner/src/Makefile.in index bde646f14b83..e39efd6bd70e 100644 --- a/layout/xul/base/src/outliner/src/Makefile.in +++ b/layout/xul/base/src/outliner/src/Makefile.in @@ -49,12 +49,16 @@ CPPSRCS = \ nsOutlinerBoxObject.cpp \ nsOutlinerColFrame.cpp \ nsOutlinerSelection.cpp \ + nsOutlinerUtils.cpp \ + nsOutlinerContentView.cpp \ $(NULL) EXPORTS = \ nsOutlinerBodyFrame.h \ nsOutlinerColFrame.h \ nsOutlinerSelection.h \ + nsOutlinerUtils.h \ + nsOutlinerContentView.h \ $(NULL) DEFINES += -D_IMPL_NS_HTML diff --git a/layout/xul/base/src/outliner/src/makefile.win b/layout/xul/base/src/outliner/src/makefile.win index 68cb639b6835..2451cad6d07d 100644 --- a/layout/xul/base/src/outliner/src/makefile.win +++ b/layout/xul/base/src/outliner/src/makefile.win @@ -46,6 +46,8 @@ CPPSRCS= \ nsOutlinerBodyFrame.cpp \ nsOutlinerColFrame.cpp \ nsOutlinerSelection.cpp \ + nsOutlinerUtils.cpp \ + nsOutlinerContentView.cpp \ $(NULL) CPP_OBJS= \ @@ -53,9 +55,12 @@ CPP_OBJS= \ .\$(OBJDIR)\nsOutlinerBodyFrame.obj \ .\$(OBJDIR)\nsOutlinerColFrame.obj \ .\$(OBJDIR)\nsOutlinerSelection.obj \ + .\$(OBJDIR)\nsOutlinerUtils.obj \ + .\$(OBJDIR)\nsOutlinerContentView.obj \ $(NULL) EXPORTS = nsOutlinerColFrame.h \ + nsOutlinerUtils.h \ $(NULL) LINCS= \ diff --git a/layout/xul/base/src/outliner/src/nsOutlinerBodyFrame.cpp b/layout/xul/base/src/outliner/src/nsOutlinerBodyFrame.cpp index 06fb814925d7..8a474fd94301 100644 --- a/layout/xul/base/src/outliner/src/nsOutlinerBodyFrame.cpp +++ b/layout/xul/base/src/outliner/src/nsOutlinerBodyFrame.cpp @@ -23,6 +23,7 @@ * Original Author: David W. Hyatt (hyatt@netscape.com) * Ben Goodger * Joe Hewitt + * Jan Varga * * 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 @@ -76,6 +77,7 @@ #include "nsNetUtil.h" #include "nsBoxLayoutState.h" #include "nsIDragService.h" +#include "nsOutlinerContentView.h" #ifdef USE_IMG2 #include "imgIRequest.h" @@ -405,17 +407,33 @@ NS_IMETHODIMP nsOutlinerBodyFrame::Reflow(nsIPresContext* aPresContext, } } - // See if there is a XUL outliner builder associated with the - // element. If so, try to make *it* be the view. + + // A content model view is always created and hooked up, + // unless there is a XULOutlinerBuilder view. + nsCOMPtr xulele = do_QueryInterface(mContent); if (xulele) { + nsCOMPtr view; + + // First, see if there is a XUL outliner builder + // associated with the element. nsCOMPtr builder; xulele->GetBuilder(getter_AddRefs(builder)); - if (builder) { - nsCOMPtr view = do_QueryInterface(builder); - if (view) - SetView(view); + if (builder) + view = do_QueryInterface(builder); + else { + // No builder, create a outliner content model view. + nsCOMPtr contentView; + NS_NewOutlinerContentView(getter_AddRefs(contentView)); + if (contentView) { + contentView->SetRoot(xulele); + view = do_QueryInterface(contentView); + } } + + // Hook up the view. + if (view) + SetView(view); } } @@ -785,39 +803,6 @@ NS_IMETHODIMP nsOutlinerBodyFrame::GetCellAt(PRInt32 aX, PRInt32 aY, PRInt32* aR PRInt32 x, y; AdjustEventCoordsToBoxCoordSpace ( aX, aY, &x, &y ); -#if 0 - // Convert our x and y coords to twips. - float pixelsToTwips = 0.0; - mPresContext->GetPixelsToTwips(&pixelsToTwips); - aX = NSToIntRound(aX * pixelsToTwips); - aY = NSToIntRound(aY * pixelsToTwips); - - // Get our box object. - nsCOMPtr doc; - mContent->GetDocument(*getter_AddRefs(doc)); - nsCOMPtr nsDoc(do_QueryInterface(doc)); - nsCOMPtr elt(do_QueryInterface(mContent)); - - nsCOMPtr boxObject; - nsDoc->GetBoxObjectFor(elt, getter_AddRefs(boxObject)); - - PRInt32 x; - PRInt32 y; - boxObject->GetX(&x); - boxObject->GetY(&y); - - x = NSToIntRound(x * pixelsToTwips); - y = NSToIntRound(y * pixelsToTwips); - - // Adjust into our coordinate space. - x = aX-x; - y = aY-y; - - // Adjust y by the inner box y, so that we're in the inner box's - // coordinate space. - y += mInnerBox.y; -#endif - // Now just mod by our total inner box height and add to our top row index. *aRow = (y/mRowHeight)+mTopRowIndex; @@ -1690,7 +1675,7 @@ NS_IMETHODIMP nsOutlinerBodyFrame::PaintRow(int aRowIndex, const nsRect& aRowRec aRenderingContext.PushState(); PRUint8 side = NS_SIDE_TOP; - PRInt32 y = rowRect.y + rowRect.height / 2; + nscoord currY = rowRect.y + rowRect.height / 2; for (PRInt32 i = 0; i < 2; i++) { nscolor color; PRBool transparent; PRBool foreground; @@ -1700,10 +1685,10 @@ NS_IMETHODIMP nsOutlinerBodyFrame::PaintRow(int aRowIndex, const nsRect& aRowRec style = borderStyle->GetBorderStyle(side); aRenderingContext.SetLineStyle(ConvertBorderStyleToLineStyle(style)); - aRenderingContext.DrawLine(rowRect.x, y, rowRect.x + rowRect.width, y); + aRenderingContext.DrawLine(rowRect.x, currY, rowRect.x + rowRect.width, currY); side = NS_SIDE_BOTTOM; - y = y + 16; + currY += 16; } PRBool clipState; diff --git a/layout/xul/base/src/outliner/src/nsOutlinerContentView.cpp b/layout/xul/base/src/outliner/src/nsOutlinerContentView.cpp new file mode 100644 index 000000000000..1265209cd9ad --- /dev/null +++ b/layout/xul/base/src/outliner/src/nsOutlinerContentView.cpp @@ -0,0 +1,1303 @@ +/* -*- 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): + * Jan Varga (varga@utcru.sk) + * + * 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 "nsReadableUtils.h" +#include "nsINameSpaceManager.h" +#include "nsHTMLAtoms.h" +#include "nsXULAtoms.h" +#include "nsIDOMDocument.h" +#include "nsOutlinerUtils.h" +#include "nsOutlinerContentView.h" + + +// A content model view implementation for the outliner. + + +#define ROW_FLAG_CONTAINER 0x01 +#define ROW_FLAG_OPEN 0x02 +#define ROW_FLAG_EMPTY 0x04 +#define ROW_FLAG_SEPARATOR 0x08 + +class Property +{ + public: + Property(nsIAtom* aAtom) + : mAtom(aAtom), mNext(nsnull) { + } + ~Property() { + delete mNext; + } + + nsCOMPtr mAtom; + Property* mNext; +}; + +class Row +{ + public: + static Row* + Create(nsFixedSizeAllocator& aAllocator, + nsIContent* aContent, PRInt32 aParentIndex) { + void* place = aAllocator.Alloc(sizeof(Row)); + return place ? ::new(place) Row(aContent, aParentIndex) : nsnull; + } + + static void + Destroy(nsFixedSizeAllocator& aAllocator, Row* aRow) { + aRow->~Row(); + aAllocator.Free(aRow, sizeof(*aRow)); + } + + Row(nsIContent* aContent, PRInt32 aParentIndex) + : mContent(aContent), mParentIndex(aParentIndex), + mSubtreeSize(0), mProperty(nsnull), mFlags(0) { + } + + ~Row() { + delete mProperty; + } + + void SetContainer(PRBool aContainer) { + aContainer ? mFlags |= ROW_FLAG_CONTAINER : mFlags &= ~ROW_FLAG_CONTAINER; + } + PRBool IsContainer() { return mFlags & ROW_FLAG_CONTAINER; } + + void SetOpen(PRBool aOpen) { + aOpen ? mFlags |= ROW_FLAG_OPEN : mFlags &= ~ROW_FLAG_OPEN; + } + PRBool IsOpen() { return mFlags & ROW_FLAG_OPEN; } + + void SetEmpty(PRBool aEmpty) { + aEmpty ? mFlags |= ROW_FLAG_EMPTY : mFlags &= ~ROW_FLAG_EMPTY; + } + PRBool IsEmpty() { return mFlags & ROW_FLAG_EMPTY; } + + void SetSeparator(PRBool aSeparator) { + aSeparator ? mFlags |= ROW_FLAG_SEPARATOR : mFlags &= ~ROW_FLAG_SEPARATOR; + } + PRBool IsSeparator() { return mFlags & ROW_FLAG_SEPARATOR; } + + void SetProperty(Property* aProperty) { + delete mProperty; + mProperty = aProperty; + } + + // Weak reference to a content item. + nsIContent* mContent; + + // The parent index of the item, set to -1 for the top level items. + PRInt32 mParentIndex; + + // Subtree size for this item. + PRInt32 mSubtreeSize; + + // List of parsed properties + Property* mProperty; + + private: + // Hide so that only Create() and Destroy() can be used to + // allocate and deallocate from the heap + static void* operator new(size_t) { return 0; } + static void operator delete(void*, size_t) {} + + // State flags + PRInt8 mFlags; +}; + + +// We don't reference count the reference to the document +// If the document goes away first, we'll be informed and we +// can drop our reference. +// If we go away first, we'll get rid of ourselves from the +// document's observer list. + +nsOutlinerContentView::nsOutlinerContentView(void) : + mBoxObject(nsnull), mSelection(nsnull), mRoot(nsnull), mDocument(nsnull) +{ + NS_INIT_ISUPPORTS(); + + static const size_t kBucketSizes[] = { + sizeof(Row) + }; + static const PRInt32 kNumBuckets = sizeof(kBucketSizes) / sizeof(size_t); + static const PRInt32 kInitialSize = 16; + + mAllocator.Init("nsOutlinerContentView", kBucketSizes, kNumBuckets, kInitialSize); +} + +nsOutlinerContentView::~nsOutlinerContentView (void) +{ + // Remove ourselfs from document's observers. + if (mDocument) + mDocument->RemoveObserver(this); +} + +nsresult +NS_NewOutlinerContentView(nsIOutlinerContentView** aResult) +{ + *aResult = new nsOutlinerContentView; + if (! *aResult) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(*aResult); + return NS_OK; +} + + +NS_IMPL_ISUPPORTS3(nsOutlinerContentView, + nsIOutlinerView, + nsIOutlinerContentView, + nsIDocumentObserver); + + +NS_IMETHODIMP +nsOutlinerContentView::GetRowCount(PRInt32* aRowCount) +{ + *aRowCount = mRows.Count(); + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::GetSelection(nsIOutlinerSelection** aSelection) +{ + NS_IF_ADDREF(*aSelection = mSelection); + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::SetSelection(nsIOutlinerSelection* aSelection) +{ + mSelection = aSelection; + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::GetRowProperties(PRInt32 aIndex, nsISupportsArray* aProperties) +{ + NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad index"); + if (aIndex < 0 || aIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + Row* row = (Row*)mRows[aIndex]; + Property* property = row->mProperty; + while (property) { + aProperties->AppendElement(property->mAtom); + property = property->mNext; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::GetCellProperties(PRInt32 aRow, const PRUnichar* aColID, nsISupportsArray* aProperties) +{ + NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad row"); + if (aRow < 0 || aRow >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + Row* row = (Row*)mRows[aRow]; + nsCOMPtr realRow; + GetImmediateChild(row->mContent, nsXULAtoms::outlinerrow, getter_AddRefs(realRow)); + if (realRow) { + nsCOMPtr cell; + GetNamedCell(realRow, aColID, getter_AddRefs(cell)); + if (cell) { + nsAutoString properties; + cell->GetAttr(kNameSpaceID_None, nsXULAtoms::properties, properties); + if (properties.Length()) + nsOutlinerUtils::TokenizeProperties(properties, aProperties); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::GetColumnProperties(const PRUnichar* aColID, nsIDOMElement* aColElt, nsISupportsArray* aProperties) +{ + nsAutoString properties; + aColElt->GetAttribute(NS_LITERAL_STRING("properties"), properties); + if (properties.Length()) + nsOutlinerUtils::TokenizeProperties(properties, aProperties); + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::IsContainer(PRInt32 aIndex, PRBool* _retval) +{ + NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad index"); + if (aIndex < 0 || aIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + *_retval = ((Row*)mRows[aIndex])->IsContainer(); + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::IsContainerOpen(PRInt32 aIndex, PRBool* _retval) +{ + NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad index"); + if (aIndex < 0 || aIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + *_retval = ((Row*)mRows[aIndex])->IsOpen(); + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::IsContainerEmpty(PRInt32 aIndex, PRBool* _retval) +{ + NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad index"); + if (aIndex < 0 || aIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + *_retval = ((Row*)mRows[aIndex])->IsEmpty(); + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::IsSeparator(PRInt32 aIndex, PRBool *_retval) +{ + NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad index"); + if (aIndex < 0 || aIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + *_retval = ((Row*)mRows[aIndex])->IsSeparator(); + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::IsSorted(PRBool *_retval) +{ + *_retval = PR_FALSE; + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::CanDropOn(PRInt32 aIndex, PRBool *_retval) +{ + NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad index"); + if (aIndex < 0 || aIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + *_retval = PR_FALSE; + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::CanDropBeforeAfter(PRInt32 aIndex, PRBool aBefore, PRBool* _retval) +{ + NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad index"); + if (aIndex < 0 || aIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + *_retval = PR_FALSE; + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::Drop(PRInt32 aRow, PRInt32 aOrientation) +{ + NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad row"); + if (aRow < 0 || aRow >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::GetParentIndex(PRInt32 aRowIndex, PRInt32* _retval) +{ + NS_PRECONDITION(aRowIndex >= 0 && aRowIndex < mRows.Count(), "bad row index"); + if (aRowIndex < 0 || aRowIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + *_retval = ((Row*)mRows[aRowIndex])->mParentIndex; + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::HasNextSibling(PRInt32 aRowIndex, PRInt32 aAfterIndex, PRBool* _retval) +{ + NS_PRECONDITION(aRowIndex >= 0 && aRowIndex < mRows.Count(), "bad row index"); + if (aRowIndex < 0 || aRowIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + // We have a next sibling if the row is not the last in the subtree. + PRInt32 parentIndex = ((Row*)mRows[aRowIndex])->mParentIndex; + if (parentIndex >= 0) { + // Compute the last index in this subtree. + PRInt32 lastIndex = parentIndex + ((Row*)mRows[parentIndex])->mSubtreeSize; + Row* row = (Row*)mRows[lastIndex]; + while (row->mParentIndex != parentIndex) { + lastIndex = row->mParentIndex; + row = (Row*)mRows[lastIndex]; + } + + *_retval = aRowIndex < lastIndex; + } + else + *_retval = aRowIndex < mRows.Count() - 1; + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::GetLevel(PRInt32 aIndex, PRInt32* _retval) +{ + NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad index"); + if (aIndex < 0 || aIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + PRInt32 level = 0; + Row* row = (Row*)mRows[aIndex]; + while (row->mParentIndex >= 0) { + level++; + row = (Row*)mRows[row->mParentIndex]; + } + *_retval = level; + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::GetCellText(PRInt32 aRow, const PRUnichar* aColID, PRUnichar** _retval) +{ + NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad row"); + if (aRow < 0 || aRow >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + *_retval = nsnull; + + Row* row = (Row*)mRows[aRow]; + nsCOMPtr realRow; + GetImmediateChild(row->mContent, nsXULAtoms::outlinerrow, getter_AddRefs(realRow)); + if (realRow) { + nsCOMPtr cell; + GetNamedCell(realRow, aColID, getter_AddRefs(cell)); + if (cell) { + nsAutoString label; + cell->GetAttr(kNameSpaceID_None, nsHTMLAtoms::label, label); + *_retval = ToNewUnicode(label); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::SetOutliner(nsIOutlinerBoxObject* aOutliner) +{ + mBoxObject = aOutliner; + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::ToggleOpenState(PRInt32 aIndex) +{ + NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad index"); + if (aIndex < 0 || aIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + Row* row = (Row*)mRows[aIndex]; + if (row->IsOpen()) { + CloseContainer(aIndex); + row->mContent->UnsetAttr(kNameSpaceID_None, nsXULAtoms::open, PR_FALSE); + } + else { + OpenContainer(aIndex); + row->mContent->SetAttr(kNameSpaceID_None, nsXULAtoms::open, NS_LITERAL_STRING("true"), PR_FALSE); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::CycleHeader(const PRUnichar* aColID, nsIDOMElement *aColElt) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::SelectionChanged() +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::CycleCell(PRInt32 aRow, const PRUnichar* aColID) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::IsEditable(PRInt32 aRow, const PRUnichar* aColID, PRBool* _retval) +{ + *_retval = PR_FALSE; + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::SetCellText(PRInt32 aRow, const PRUnichar* aColID, const PRUnichar* aValue) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::PerformAction(const PRUnichar* aAction) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::PerformActionOnRow(const PRUnichar* aAction, PRInt32 aRow) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::PerformActionOnCell(const PRUnichar* aAction, PRInt32 aRowconst, const PRUnichar* aColID) +{ + return NS_OK; +} + + +NS_IMETHODIMP +nsOutlinerContentView::GetRoot(nsIDOMElement** aRoot) +{ + if (mRoot) + mRoot->QueryInterface(NS_GET_IID(nsIDOMElement), (void**)aRoot); + else + *aRoot = nsnull; + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::SetRoot(nsIDOMElement* aRoot) +{ + if (mRoot) { + // Remove ourselfs from document's observers. + if (mDocument) { + mDocument->RemoveObserver(this); + mDocument = nsnull; + } + + ClearRows(); + } + + if (aRoot) + mRoot = do_QueryInterface(aRoot); + + if (mRoot) { + // Add ourselfs to document's observers. + nsCOMPtr document; + mRoot->GetDocument(*getter_AddRefs(document)); + if (document) { + document->AddObserver(this); + mDocument = document; + } + + PRInt32 index = 0; + Serialize(mRoot, -1, &index, mRows); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::GetItemAtIndex(PRInt32 aIndex, nsIDOMElement** _retval) +{ + NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad index"); + if (aIndex < 0 || aIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + Row* row = (Row*)mRows[aIndex]; + row->mContent->QueryInterface(NS_GET_IID(nsIDOMElement), (void**)_retval); + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::GetIndexOfItem(nsIDOMElement* aItem, PRInt32* _retval) +{ + nsCOMPtr content = do_QueryInterface(aItem); + *_retval = FindContent(content); + + return NS_OK; +} + + +NS_IMETHODIMP +nsOutlinerContentView::BeginUpdate(nsIDocument *aDocument) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::EndUpdate(nsIDocument *aDocument) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::BeginLoad(nsIDocument *aDocument) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::EndLoad(nsIDocument *aDocument) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::BeginReflow(nsIDocument *aDocument, nsIPresShell* aShell) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::EndReflow(nsIDocument *aDocument, nsIPresShell* aShell) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::ContentChanged(nsIDocument *aDocument, + nsIContent* aContent, + nsISupports* aSubContent) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::ContentStatesChanged(nsIDocument* aDocument, + nsIContent* aContent1, + nsIContent* aContent2) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::AttributeChanged(nsIDocument *aDocument, + nsIContent* aContent, + PRInt32 aNameSpaceID, + nsIAtom* aAttribute, + PRInt32 aModType, + PRInt32 aHint) +{ + // First, get the element on which the attribute has changed + // and then try to find content item in our array of rows. + nsCOMPtr tag; + aContent->GetTag(*getter_AddRefs(tag)); + + if (tag == nsXULAtoms::outlinercol) { + if (aAttribute == nsXULAtoms::properties) + mBoxObject->Invalidate(); + } + else if (tag == nsXULAtoms::outlineritem) { + if (aAttribute == nsXULAtoms::open) { + PRInt32 index = FindContent(aContent); + if (index >= 0) { + Row* row = (Row*)mRows[index]; + nsAutoString open; + aContent->GetAttr(kNameSpaceID_None, nsXULAtoms::open, open); + PRBool isOpen = open.Equals(NS_LITERAL_STRING("true")); + PRBool wasOpen = row->IsOpen(); + if (! isOpen && wasOpen) + CloseContainer(index); + else if (isOpen && ! wasOpen) + OpenContainer(index); + } + } + } + else if (tag == nsXULAtoms::outlinerseparator) { + if (aAttribute == nsXULAtoms::properties) { + PRInt32 index = FindContent(aContent); + if (index >= 0) { + Row* row = (Row*)mRows[index]; + row->SetProperty(nsnull); + ParseProperties(aContent, &row->mProperty); + mBoxObject->InvalidateRow(index); + } + } + } + else if (tag == nsXULAtoms::outlinerrow) { + if (aAttribute == nsXULAtoms::properties) { + nsCOMPtr parent; + aContent->GetParent(*getter_AddRefs(parent)); + if (parent) { + PRInt32 index = FindContent(parent); + if (index >= 0) { + Row* row = (Row*)mRows[index]; + row->SetProperty(nsnull); + ParseProperties(aContent, &row->mProperty); + mBoxObject->InvalidateRow(index); + } + } + } + } + else if (tag == nsXULAtoms::outlinercell) { + if (aAttribute == nsXULAtoms::ref || + aAttribute == nsHTMLAtoms::label || + aAttribute == nsXULAtoms::properties) { + nsCOMPtr parent; + aContent->GetParent(*getter_AddRefs(parent)); + if (parent) { + nsCOMPtr grandParent; + parent->GetParent(*getter_AddRefs(grandParent)); + if (grandParent) { + PRInt32 index = FindContent(grandParent); + if (index >= 0) { + // XXX Should we make an effort to invalidate only cell ? + mBoxObject->InvalidateRow(index); + } + } + } + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::ContentAppended(nsIDocument *aDocument, + nsIContent* aContainer, + PRInt32 aNewIndexInContainer) +{ + nsCOMPtr child; + aContainer->ChildAt(aNewIndexInContainer, *getter_AddRefs(child)); + ContentInserted(aDocument, aContainer, child, aNewIndexInContainer); + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::ContentInserted(nsIDocument *aDocument, + nsIContent* aContainer, + nsIContent* aChild, + PRInt32 aIndexInContainer) +{ + nsCOMPtr childTag; + aChild->GetTag(*getter_AddRefs(childTag)); + + if (childTag == nsXULAtoms::outlineritem || + childTag == nsXULAtoms::outlinerseparator) { + PRInt32 parentIndex = -1; + if (aContainer != mRoot) { + nsCOMPtr parent; + aContainer->GetParent(*getter_AddRefs(parent)); + parentIndex = FindContent(parent); + } + + PRInt32 index = 0; + GetIndexInSubtree(aContainer, aChild, &index); + + PRInt32 count; + InsertRow(parentIndex, index, aChild, &count); + mBoxObject->RowCountChanged(parentIndex + index + 1, count); + } + else if (childTag == nsXULAtoms::outlinerchildren) { + PRInt32 index = FindContent(aContainer); + if (index >= 0) { + Row* row = (Row*)mRows[index]; + row->SetEmpty(PR_FALSE); + mBoxObject->InvalidateRow(index); + if (row->IsContainer() && row->IsOpen()) { + PRInt32 count; + EnsureSubtree(index, &count); + mBoxObject->RowCountChanged(index + 1, count); + } + } + } + else if (childTag == nsXULAtoms::outlinerrow) { + PRInt32 index = FindContent(aContainer); + if (index >= 0) + mBoxObject->InvalidateRow(index); + } + else if (childTag == nsXULAtoms::outlinercell) { + nsCOMPtr parent; + aContainer->GetParent(*getter_AddRefs(parent)); + if (parent) { + PRInt32 index = FindContent(parent); + if (index >= 0) + mBoxObject->InvalidateRow(index); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::ContentReplaced(nsIDocument *aDocument, + nsIContent* aContainer, + nsIContent* aOldChild, + nsIContent* aNewChild, + PRInt32 aIndexInContainer) +{ + ContentRemoved(aDocument, aContainer, aOldChild, aIndexInContainer); + ContentInserted(aDocument, aContainer, aNewChild, aIndexInContainer); + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::ContentRemoved(nsIDocument *aDocument, + nsIContent* aContainer, + nsIContent* aChild, + PRInt32 aIndexInContainer) +{ + nsCOMPtr tag; + aChild->GetTag(*getter_AddRefs(tag)); + + if (tag == nsXULAtoms::outlineritem || + tag == nsXULAtoms::outlinerseparator) { + PRInt32 index = FindContent(aChild); + if (index >= 0) { + PRInt32 count; + RemoveRow(index, &count); + mBoxObject->RowCountChanged(index, -count); + } + } + else if (tag == nsXULAtoms::outlinerchildren) { + PRInt32 index = FindContent(aContainer); + if (index >= 0) { + Row* row = (Row*)mRows[index]; + row->SetEmpty(PR_TRUE); + PRInt32 count; + RemoveSubtree(index, &count); + // Invalidate also the row to update twisty. + mBoxObject->InvalidateRow(index); + mBoxObject->RowCountChanged(index + 1, -count); + } + } + else if (tag == nsXULAtoms::outlinerrow) { + PRInt32 index = FindContent(aContainer); + if (index >= 0) + mBoxObject->InvalidateRow(index); + } + else if (tag == nsXULAtoms::outlinercell) { + nsCOMPtr parent; + aContainer->GetParent(*getter_AddRefs(parent)); + if (parent) { + PRInt32 index = FindContent(parent); + if (index >= 0) + mBoxObject->InvalidateRow(index); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::StyleSheetAdded(nsIDocument *aDocument, + nsIStyleSheet* aStyleSheet) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::StyleSheetRemoved(nsIDocument *aDocument, + nsIStyleSheet* aStyleSheet) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::StyleSheetDisabledStateChanged(nsIDocument *aDocument, + nsIStyleSheet* aStyleSheet, + PRBool aDisabled) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::StyleRuleChanged(nsIDocument *aDocument, + nsIStyleSheet* aStyleSheet, + nsIStyleRule* aStyleRule, + PRInt32 aHint) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::StyleRuleAdded(nsIDocument *aDocument, + nsIStyleSheet* aStyleSheet, + nsIStyleRule* aStyleRule) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::StyleRuleRemoved(nsIDocument *aDocument, + nsIStyleSheet* aStyleSheet, + nsIStyleRule* aStyleRule) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::DocumentWillBeDestroyed(nsIDocument *aDocument) +{ + // Remove ourselfs from document's observers. + if (mDocument) { + mDocument->RemoveObserver(this); + mDocument = nsnull; + } + + ClearRows(); + + return NS_OK; +} + + +// Recursively serialize content, starting with aContent. +void +nsOutlinerContentView::Serialize(nsIContent* aContent, PRInt32 aParentIndex, PRInt32* aIndex, nsVoidArray& aRows) +{ + PRInt32 childCount; + aContent->ChildCount(childCount); + for (PRInt32 i = 0; i < childCount; i++) { + nsCOMPtr content; + aContent->ChildAt(i, *getter_AddRefs(content)); + nsCOMPtr tag; + content->GetTag(*getter_AddRefs(tag)); + PRInt32 count = aRows.Count(); + if (tag == nsXULAtoms::outlineritem) + SerializeItem(content, aParentIndex, aIndex, aRows); + else if (tag == nsXULAtoms::outlinerseparator) + SerializeSeparator(content, aParentIndex, aIndex, aRows); + *aIndex += aRows.Count() - count; + } +} + +void +nsOutlinerContentView::SerializeItem(nsIContent* aContent, PRInt32 aParentIndex, PRInt32* aIndex, nsVoidArray& aRows) +{ + Row* row = Row::Create(mAllocator, aContent, aParentIndex); + aRows.AppendElement(row); + + nsCOMPtr realRow; + GetImmediateChild(aContent, nsXULAtoms::outlinerrow, getter_AddRefs(realRow)); + if (realRow) + ParseProperties(realRow, &row->mProperty); + + nsAutoString container; + aContent->GetAttr(kNameSpaceID_None, nsXULAtoms::container, container); + if (container.Equals(NS_LITERAL_STRING("true"))) { + row->SetContainer(PR_TRUE); + nsAutoString open; + aContent->GetAttr(kNameSpaceID_None, nsXULAtoms::open, open); + if (open.Equals(NS_LITERAL_STRING("true"))) { + row->SetOpen(PR_TRUE); + nsCOMPtr child; + GetImmediateChild(aContent, nsXULAtoms::outlinerchildren, getter_AddRefs(child)); + if (child) { + // Now, recursively serialize our child. + PRInt32 count = aRows.Count(); + PRInt32 index = 0; + Serialize(child, aParentIndex + *aIndex + 1, &index, aRows); + row->mSubtreeSize += aRows.Count() - count; + } + else + row->SetEmpty(PR_TRUE); + } + } +} + +void +nsOutlinerContentView::SerializeSeparator(nsIContent* aContent, PRInt32 aParentIndex, PRInt32* aIndex, nsVoidArray& aRows) +{ + Row* row = Row::Create(mAllocator, aContent, aParentIndex); + row->SetSeparator(PR_TRUE); + aRows.AppendElement(row); + + ParseProperties(aContent, &row->mProperty); +} + +void +nsOutlinerContentView::GetIndexInSubtree(nsIContent* aContainer, nsIContent* aContent, PRInt32* aIndex) +{ + PRInt32 childCount; + aContainer->ChildCount(childCount); + for (PRInt32 i = 0; i < childCount; i++) { + nsCOMPtr content; + aContainer->ChildAt(i, *getter_AddRefs(content)); + if (content == aContent) + break; + + nsCOMPtr tag; + content->GetTag(*getter_AddRefs(tag)); + if (tag == nsXULAtoms::outlineritem) { + (*aIndex)++; + nsAutoString container; + content->GetAttr(kNameSpaceID_None, nsXULAtoms::container, container); + if (container.Equals(NS_LITERAL_STRING("true"))) { + nsAutoString open; + content->GetAttr(kNameSpaceID_None, nsXULAtoms::open, open); + if (open.Equals(NS_LITERAL_STRING("true"))) { + nsCOMPtr child; + GetImmediateChild(content, nsXULAtoms::outlinerchildren, getter_AddRefs(child)); + if (child) + GetIndexInSubtree(child, aContent, aIndex); + } + } + } + else if (tag == nsXULAtoms::outlinerseparator) + (*aIndex)++; + } +} + +void +nsOutlinerContentView::EnsureSubtree(PRInt32 aIndex, PRInt32* aCount) +{ + Row* row = (Row*)mRows[aIndex]; + nsCOMPtr child; + GetImmediateChild(row->mContent, nsXULAtoms::outlinerchildren, getter_AddRefs(child)); + if (! child) { + *aCount = 0; + return; + } + + nsAutoVoidArray rows; + PRInt32 index = 0; + Serialize(child, aIndex, &index, rows); + mRows.InsertElementsAt(rows, aIndex + 1); + PRInt32 count = rows.Count(); + + row->mSubtreeSize += count; + UpdateSubtreeSizes(row->mParentIndex, count); + + // Update parent indexes, but skip newly added rows. + // They already have correct values. + UpdateParentIndexes(aIndex, count, count); + + *aCount = count; +} + +void +nsOutlinerContentView::RemoveSubtree(PRInt32 aIndex, PRInt32* aCount) +{ + Row* row = (Row*)mRows[aIndex]; + PRInt32 count = row->mSubtreeSize; + + for(PRInt32 i = 0; i < count; i++) { + Row* nextRow = (Row*)mRows[aIndex + i + 1]; + Row::Destroy(mAllocator, nextRow); + } + mRows.RemoveElementsAt(aIndex + 1, count); + + row->mSubtreeSize -= count; + UpdateSubtreeSizes(row->mParentIndex, -count); + + UpdateParentIndexes(aIndex, 0, -count); + + *aCount = count; +} + +void +nsOutlinerContentView::InsertRow(PRInt32 aParentIndex, PRInt32 aIndex, nsIContent* aContent, PRInt32* aCount) +{ + nsAutoVoidArray rows; + nsCOMPtr tag; + aContent->GetTag(*getter_AddRefs(tag)); + if (tag == nsXULAtoms::outlineritem) + SerializeItem(aContent, aParentIndex, &aIndex, rows); + else if (tag == nsXULAtoms::outlinerseparator) + SerializeSeparator(aContent, aParentIndex, &aIndex, rows); + mRows.InsertElementsAt(rows, aParentIndex + aIndex + 1); + PRInt32 count = rows.Count(); + + UpdateSubtreeSizes(aParentIndex, count); + + // Update parent indexes, but skip added rows. + // They already have correct values. + UpdateParentIndexes(aParentIndex + aIndex, count + 1, count); + + *aCount = count; +} + +void +nsOutlinerContentView::RemoveRow(PRInt32 aIndex, PRInt32* aCount) +{ + Row* row = (Row*)mRows[aIndex]; + PRInt32 count = row->mSubtreeSize + 1; + PRInt32 parentIndex = row->mParentIndex; + + Row::Destroy(mAllocator, row); + for(PRInt32 i = 1; i < count; i++) { + Row* nextRow = (Row*)mRows[aIndex + i]; + Row::Destroy(mAllocator, nextRow); + } + mRows.RemoveElementsAt(aIndex, count); + + UpdateSubtreeSizes(parentIndex, -count); + + UpdateParentIndexes(aIndex, 0, -count); + + *aCount = count; +} + +void +nsOutlinerContentView::ClearRows() +{ + for (PRInt32 i = 0; i < mRows.Count(); i++) + Row::Destroy(mAllocator, (Row*)mRows[i]); + mRows.Clear(); + mRoot = nsnull; +} + +void +nsOutlinerContentView::OpenContainer(PRInt32 aIndex) +{ + Row* row = (Row*)mRows[aIndex]; + row->SetOpen(PR_TRUE); + + PRInt32 count; + EnsureSubtree(aIndex, &count); + mBoxObject->RowCountChanged(aIndex + 1, count); +} + + +void +nsOutlinerContentView::CloseContainer(PRInt32 aIndex) +{ + Row* row = (Row*)mRows[aIndex]; + row->SetOpen(PR_FALSE); + + PRInt32 count; + RemoveSubtree(aIndex, &count); + mBoxObject->RowCountChanged(aIndex + 1, -count); +} + +PRInt32 +nsOutlinerContentView::FindContent(nsIContent* aContent) +{ + for (PRInt32 i = 0; i < mRows.Count(); i++) + if (((Row*)mRows[i])->mContent == aContent) + return i; + + return -1; +} + +void +nsOutlinerContentView::UpdateSubtreeSizes(PRInt32 aParentIndex, PRInt32 count) +{ + while (aParentIndex >= 0) { + Row* row = (Row*)mRows[aParentIndex]; + row->mSubtreeSize += count; + aParentIndex = row->mParentIndex; + } +} + +void +nsOutlinerContentView::UpdateParentIndexes(PRInt32 aIndex, PRInt32 aSkip, PRInt32 aCount) +{ + PRInt32 count = mRows.Count(); + for (PRInt32 i = aIndex + aSkip; i < count; i++) { + Row* row = (Row*)mRows[i]; + if (row->mParentIndex > aIndex) + row->mParentIndex += aCount; + } +} + +nsresult +nsOutlinerContentView::GetImmediateChild(nsIContent* aContainer, nsIAtom* aTag, nsIContent** aResult) +{ + PRInt32 childCount; + aContainer->ChildCount(childCount); + for (PRInt32 i = 0; i < childCount; i++) { + nsCOMPtr child; + aContainer->ChildAt(i, *getter_AddRefs(child)); + nsCOMPtr tag; + child->GetTag(*getter_AddRefs(tag)); + if (tag == aTag) { + NS_ADDREF(*aResult = child); + return NS_OK; + } + } + + *aResult = nsnull; + return NS_OK; +} + +nsresult +nsOutlinerContentView::GetColIndex(const PRUnichar* aColID, PRInt32* aResult) +{ + *aResult = -1; + + // First, try to find col index by already set "colIndex" attribute. + nsCOMPtr domDocument = do_QueryInterface(mDocument); + nsCOMPtr domElement; + domDocument->GetElementById(nsDependentString(aColID), getter_AddRefs(domElement)); + if (domElement) { + nsAutoString colIndexValue; + domElement->GetAttribute(NS_LITERAL_STRING("colIndex"), colIndexValue); + if (colIndexValue.Length()) { + PRInt32 rv; + *aResult = colIndexValue.ToInteger(&rv); + } + } + + if (*aResult == -1) { + // No "colIndex" attribute, traverse through cols + nsCOMPtr parent; + mRoot->GetParent(*getter_AddRefs(parent)); + if (parent) { + PRInt32 childCount; + parent->ChildCount(childCount); + PRInt32 j = 0; + for (PRInt32 i = 0; i < childCount; i++) { + nsCOMPtr child; + parent->ChildAt(i, *getter_AddRefs(child)); + nsCOMPtr tag; + child->GetTag(*getter_AddRefs(tag)); + if (tag == nsXULAtoms::outlinercol) { + nsAutoString id; + child->GetAttr(kNameSpaceID_None, nsHTMLAtoms::id, id); + if (id.Equals(aColID)) { + // Found it, set "colIndex" attribute to speed up next call. + nsCOMPtr colIndexAtom; + colIndexAtom = dont_AddRef(NS_NewAtom(NS_LITERAL_STRING("colIndex"))); + nsAutoString colIndexValue; + colIndexValue.AppendInt(j); + child->SetAttr(kNameSpaceID_None, colIndexAtom, colIndexValue, PR_FALSE); + *aResult = j; + break; + } + j++; + } + } + } + } + + return NS_OK; +} + +nsresult +nsOutlinerContentView::GetNamedCell(nsIContent* aContainer, const PRUnichar* aColID, nsIContent** aResult) +{ + PRInt32 colIndex; + GetColIndex(aColID, &colIndex); + + // Traverse through cells, try to find the cell by "ref" attribute or by cell + // index in a row. "ref" attribute has higher priority. + *aResult = nsnull; + PRInt32 childCount; + aContainer->ChildCount(childCount); + PRInt32 j = 0; + for (PRInt32 i = 0; i < childCount; i++) { + nsCOMPtr cell; + aContainer->ChildAt(i, *getter_AddRefs(cell)); + nsCOMPtr tag; + cell->GetTag(*getter_AddRefs(tag)); + if (tag == nsXULAtoms::outlinercell) { + nsAutoString ref; + cell->GetAttr(kNameSpaceID_None, nsXULAtoms::ref, ref); + if (ref.Equals(aColID)) { + *aResult = cell; + break; + } + else if (j == colIndex) + *aResult = cell; + j++; + } + } + NS_IF_ADDREF(*aResult); + + return NS_OK; +} + +nsresult +nsOutlinerContentView::ParseProperties(nsIContent* aContent, Property** aProperty) +{ + nsAutoString properties; + aContent->GetAttr(kNameSpaceID_None, nsXULAtoms::properties, properties); + if (properties.Length()) { + Property* lastProperty = *aProperty; + + nsAString::const_iterator end; + properties.EndReading(end); + + nsAString::const_iterator iter; + properties.BeginReading(iter); + + do { + // Skip whitespace + while (iter != end && nsCRT::IsAsciiSpace(*iter)) + ++iter; + + // If only whitespace, we're done + if (iter == end) + break; + + // Note the first non-whitespace character + nsAString::const_iterator first = iter; + + // Advance to the next whitespace character + while (iter != end && ! nsCRT::IsAsciiSpace(*iter)) + ++iter; + + nsCOMPtr atom = dont_AddRef(NS_NewAtom(Substring(first, iter))); + Property* newProperty = new Property(atom); + if (lastProperty) + lastProperty->mNext = newProperty; + else + *aProperty = newProperty; + lastProperty = newProperty; + + } while (iter != end); + } + + return NS_OK; +} diff --git a/layout/xul/base/src/outliner/src/nsOutlinerContentView.h b/layout/xul/base/src/outliner/src/nsOutlinerContentView.h new file mode 100644 index 000000000000..bbad1611f5dc --- /dev/null +++ b/layout/xul/base/src/outliner/src/nsOutlinerContentView.h @@ -0,0 +1,194 @@ +/* -*- 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): + * Jan Varga (varga@utcru.sk) + * + * 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 ***** */ + +#ifndef nsOutlinerContentView_h__ +#define nsOutlinerContentView_h__ + +#include "nsCOMPtr.h" +#include "nsFixedSizeAllocator.h" +#include "nsVoidArray.h" +#include "nsIContent.h" +#include "nsIDocument.h" +#include "nsIDocumentObserver.h" +#include "nsIOutlinerView.h" +#include "nsIOutlinerBoxObject.h" +#include "nsIOutlinerSelection.h" +#include "nsIOutlinerContentView.h" + +class Property; + +class nsOutlinerContentView : public nsIOutlinerView, + public nsIOutlinerContentView, + public nsIDocumentObserver +{ + public: + nsOutlinerContentView(void); + + virtual ~nsOutlinerContentView(void); + + friend nsresult NS_NewOutlinerContentView(nsIOutlinerContentView** aResult); + + NS_DECL_ISUPPORTS + + NS_DECL_NSIOUTLINERVIEW + + NS_DECL_NSIOUTLINERCONTENTVIEW + + // nsIDocumentObserver + NS_IMETHOD BeginUpdate(nsIDocument *aDocument); + + NS_IMETHOD EndUpdate(nsIDocument *aDocument); + + NS_IMETHOD BeginLoad(nsIDocument *aDocument); + + NS_IMETHOD EndLoad(nsIDocument *aDocument); + + NS_IMETHOD BeginReflow(nsIDocument *aDocument, nsIPresShell* aShell); + + NS_IMETHOD EndReflow(nsIDocument *aDocument, nsIPresShell* aShell); + + NS_IMETHOD ContentChanged(nsIDocument *aDocument, + nsIContent* aContent, + nsISupports* aSubContent); + + NS_IMETHOD ContentStatesChanged(nsIDocument* aDocument, + nsIContent* aContent1, + nsIContent* aContent2); + + NS_IMETHOD AttributeChanged(nsIDocument *aDocument, + nsIContent* aContent, + PRInt32 aNameSpaceID, + nsIAtom* aAttribute, + PRInt32 aModType, + PRInt32 aHint); + + NS_IMETHOD ContentAppended(nsIDocument *aDocument, + nsIContent* aContainer, + PRInt32 aNewIndexInContainer); + + NS_IMETHOD ContentInserted(nsIDocument *aDocument, + nsIContent* aContainer, + nsIContent* aChild, + PRInt32 aIndexInContainer); + + NS_IMETHOD ContentReplaced(nsIDocument *aDocument, + nsIContent* aContainer, + nsIContent* aOldChild, + nsIContent* aNewChild, + PRInt32 aIndexInContainer); + + NS_IMETHOD ContentRemoved(nsIDocument *aDocument, + nsIContent* aContainer, + nsIContent* aChild, + PRInt32 aIndexInContainer); + + NS_IMETHOD StyleSheetAdded(nsIDocument *aDocument, + nsIStyleSheet* aStyleSheet); + + NS_IMETHOD StyleSheetRemoved(nsIDocument *aDocument, + nsIStyleSheet* aStyleSheet); + + NS_IMETHOD StyleSheetDisabledStateChanged(nsIDocument *aDocument, + nsIStyleSheet* aStyleSheet, + PRBool aDisabled); + + NS_IMETHOD StyleRuleChanged(nsIDocument *aDocument, + nsIStyleSheet* aStyleSheet, + nsIStyleRule* aStyleRule, + PRInt32 aHint); + + NS_IMETHOD StyleRuleAdded(nsIDocument *aDocument, + nsIStyleSheet* aStyleSheet, + nsIStyleRule* aStyleRule); + + NS_IMETHOD StyleRuleRemoved(nsIDocument *aDocument, + nsIStyleSheet* aStyleSheet, + nsIStyleRule* aStyleRule); + + NS_IMETHOD DocumentWillBeDestroyed(nsIDocument *aDocument); + + protected: + // Recursive methods which deal with serializing of nested content. + void Serialize(nsIContent* aContent, PRInt32 aParentIndex, PRInt32* aIndex, nsVoidArray& aRows); + + void SerializeItem(nsIContent* aContent, PRInt32 aParentIndex, PRInt32* aIndex, nsVoidArray& aRows); + + void SerializeSeparator(nsIContent* aContent, PRInt32 aParentIndex, PRInt32* aIndex, nsVoidArray& aRows); + + void GetIndexInSubtree(nsIContent* aContainer, nsIContent* aContent, PRInt32* aResult); + + // Helper methods which we use to manage our plain array of rows. + void EnsureSubtree(PRInt32 aIndex, PRInt32* aCount); + + void RemoveSubtree(PRInt32 aIndex, PRInt32* aCount); + + void InsertRow(PRInt32 aParentIndex, PRInt32 aIndex, nsIContent* aContent, PRInt32* aCount); + + void RemoveRow(PRInt32 aIndex, PRInt32* aCount); + + void ClearRows(); + + void OpenContainer(PRInt32 aIndex); + + void CloseContainer(PRInt32 aIndex); + + PRInt32 FindContent(nsIContent* aContent); + + void UpdateSubtreeSizes(PRInt32 aIndex, PRInt32 aCount); + + void UpdateParentIndexes(PRInt32 aIndex, PRInt32 aSkip, PRInt32 aCount); + + // Content helpers. + nsresult GetImmediateChild(nsIContent* aContainer, nsIAtom* aTag, nsIContent** aResult); + + nsresult GetColIndex(const PRUnichar* aColID, PRInt32* aResult); + + nsresult GetNamedCell(nsIContent* aContainer, const PRUnichar* aColID, nsIContent** aResult); + + nsresult ParseProperties(nsIContent* aContent, Property** aProperty); + + private: + nsCOMPtr mBoxObject; + nsCOMPtr mSelection; + nsCOMPtr mRoot; + nsIDocument* mDocument; // WEAK + nsFixedSizeAllocator mAllocator; + nsVoidArray mRows; +}; + +#endif // nsOutlinerContentView_h__ diff --git a/layout/xul/base/src/outliner/src/nsOutlinerUtils.cpp b/layout/xul/base/src/outliner/src/nsOutlinerUtils.cpp new file mode 100644 index 000000000000..0a05d503abb8 --- /dev/null +++ b/layout/xul/base/src/outliner/src/nsOutlinerUtils.cpp @@ -0,0 +1,80 @@ +/* -*- 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): + * Chris Waterson + * + * 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 "nsOutlinerUtils.h" + +nsresult +nsOutlinerUtils::TokenizeProperties(const nsAString& aProperties, nsISupportsArray* aPropertiesArray) +{ + NS_PRECONDITION(aPropertiesArray != nsnull, "null ptr"); + if (! aPropertiesArray) + return NS_ERROR_NULL_POINTER; + + nsAString::const_iterator end; + aProperties.EndReading(end); + + nsAString::const_iterator iter; + aProperties.BeginReading(iter); + + do { + // Skip whitespace + while (iter != end && nsCRT::IsAsciiSpace(*iter)) + ++iter; + + // If only whitespace, we're done + if (iter == end) + break; + + // Note the first non-whitespace character + nsAString::const_iterator first = iter; + + // Advance to the next whitespace character + while (iter != end && ! nsCRT::IsAsciiSpace(*iter)) + ++iter; + + // XXX this would be nonsensical + NS_ASSERTION(iter != first, "eh? something's wrong here"); + if (iter == first) + break; + + nsCOMPtr atom = dont_AddRef(NS_NewAtom(Substring(first, iter))); + aPropertiesArray->AppendElement(atom); + } while (iter != end); + + return NS_OK; +} diff --git a/layout/xul/base/src/outliner/src/nsOutlinerUtils.h b/layout/xul/base/src/outliner/src/nsOutlinerUtils.h new file mode 100644 index 000000000000..a657193d06db --- /dev/null +++ b/layout/xul/base/src/outliner/src/nsOutlinerUtils.h @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 3; 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): + * Chris Waterson + * + * 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 ***** */ + +#ifndef nsOutlinerUtils_h__ +#define nsOutlinerUtils_h__ + +#include "nsString.h" +#include "nsISupportsArray.h" + +class nsOutlinerUtils +{ + public: + /** + * Parse a whitespace separated list of properties into an array + * of atoms. + */ + static nsresult + TokenizeProperties(const nsAString& aProperties, nsISupportsArray* aPropertiesArray); +}; + +#endif // nsOutlinerUtils_h__ diff --git a/layout/xul/base/src/tree/public/nsITreeContentView.idl b/layout/xul/base/src/tree/public/nsITreeContentView.idl new file mode 100644 index 000000000000..8b5a9143c19b --- /dev/null +++ b/layout/xul/base/src/tree/public/nsITreeContentView.idl @@ -0,0 +1,59 @@ +/* -*- 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.org 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): + * Jan Varga (varga@utcru.sk) + * + * 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 "nsISupports.idl" +#include "nsIDOMElement.idl" + +[scriptable, uuid(F066F69A-6FFE-4117-A85C-ABE9D15C6BBC)] +interface nsIOutlinerContentView : nsISupports +{ + /** + * The element in the DOM which this view uses as root content. + */ + attribute nsIDOMElement root; + + /** + * Retrieve the content item associated with the specified index. + */ + nsIDOMElement getItemAtIndex(in long index); + + /** + * Retrieve the index associated with the specified content item. + */ + long getIndexOfItem(in nsIDOMElement item); +}; diff --git a/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp b/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp index 06fb814925d7..8a474fd94301 100644 --- a/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp +++ b/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp @@ -23,6 +23,7 @@ * Original Author: David W. Hyatt (hyatt@netscape.com) * Ben Goodger * Joe Hewitt + * Jan Varga * * 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 @@ -76,6 +77,7 @@ #include "nsNetUtil.h" #include "nsBoxLayoutState.h" #include "nsIDragService.h" +#include "nsOutlinerContentView.h" #ifdef USE_IMG2 #include "imgIRequest.h" @@ -405,17 +407,33 @@ NS_IMETHODIMP nsOutlinerBodyFrame::Reflow(nsIPresContext* aPresContext, } } - // See if there is a XUL outliner builder associated with the - // element. If so, try to make *it* be the view. + + // A content model view is always created and hooked up, + // unless there is a XULOutlinerBuilder view. + nsCOMPtr xulele = do_QueryInterface(mContent); if (xulele) { + nsCOMPtr view; + + // First, see if there is a XUL outliner builder + // associated with the element. nsCOMPtr builder; xulele->GetBuilder(getter_AddRefs(builder)); - if (builder) { - nsCOMPtr view = do_QueryInterface(builder); - if (view) - SetView(view); + if (builder) + view = do_QueryInterface(builder); + else { + // No builder, create a outliner content model view. + nsCOMPtr contentView; + NS_NewOutlinerContentView(getter_AddRefs(contentView)); + if (contentView) { + contentView->SetRoot(xulele); + view = do_QueryInterface(contentView); + } } + + // Hook up the view. + if (view) + SetView(view); } } @@ -785,39 +803,6 @@ NS_IMETHODIMP nsOutlinerBodyFrame::GetCellAt(PRInt32 aX, PRInt32 aY, PRInt32* aR PRInt32 x, y; AdjustEventCoordsToBoxCoordSpace ( aX, aY, &x, &y ); -#if 0 - // Convert our x and y coords to twips. - float pixelsToTwips = 0.0; - mPresContext->GetPixelsToTwips(&pixelsToTwips); - aX = NSToIntRound(aX * pixelsToTwips); - aY = NSToIntRound(aY * pixelsToTwips); - - // Get our box object. - nsCOMPtr doc; - mContent->GetDocument(*getter_AddRefs(doc)); - nsCOMPtr nsDoc(do_QueryInterface(doc)); - nsCOMPtr elt(do_QueryInterface(mContent)); - - nsCOMPtr boxObject; - nsDoc->GetBoxObjectFor(elt, getter_AddRefs(boxObject)); - - PRInt32 x; - PRInt32 y; - boxObject->GetX(&x); - boxObject->GetY(&y); - - x = NSToIntRound(x * pixelsToTwips); - y = NSToIntRound(y * pixelsToTwips); - - // Adjust into our coordinate space. - x = aX-x; - y = aY-y; - - // Adjust y by the inner box y, so that we're in the inner box's - // coordinate space. - y += mInnerBox.y; -#endif - // Now just mod by our total inner box height and add to our top row index. *aRow = (y/mRowHeight)+mTopRowIndex; @@ -1690,7 +1675,7 @@ NS_IMETHODIMP nsOutlinerBodyFrame::PaintRow(int aRowIndex, const nsRect& aRowRec aRenderingContext.PushState(); PRUint8 side = NS_SIDE_TOP; - PRInt32 y = rowRect.y + rowRect.height / 2; + nscoord currY = rowRect.y + rowRect.height / 2; for (PRInt32 i = 0; i < 2; i++) { nscolor color; PRBool transparent; PRBool foreground; @@ -1700,10 +1685,10 @@ NS_IMETHODIMP nsOutlinerBodyFrame::PaintRow(int aRowIndex, const nsRect& aRowRec style = borderStyle->GetBorderStyle(side); aRenderingContext.SetLineStyle(ConvertBorderStyleToLineStyle(style)); - aRenderingContext.DrawLine(rowRect.x, y, rowRect.x + rowRect.width, y); + aRenderingContext.DrawLine(rowRect.x, currY, rowRect.x + rowRect.width, currY); side = NS_SIDE_BOTTOM; - y = y + 16; + currY += 16; } PRBool clipState; diff --git a/layout/xul/base/src/tree/src/nsTreeContentView.cpp b/layout/xul/base/src/tree/src/nsTreeContentView.cpp new file mode 100644 index 000000000000..1265209cd9ad --- /dev/null +++ b/layout/xul/base/src/tree/src/nsTreeContentView.cpp @@ -0,0 +1,1303 @@ +/* -*- 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): + * Jan Varga (varga@utcru.sk) + * + * 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 "nsReadableUtils.h" +#include "nsINameSpaceManager.h" +#include "nsHTMLAtoms.h" +#include "nsXULAtoms.h" +#include "nsIDOMDocument.h" +#include "nsOutlinerUtils.h" +#include "nsOutlinerContentView.h" + + +// A content model view implementation for the outliner. + + +#define ROW_FLAG_CONTAINER 0x01 +#define ROW_FLAG_OPEN 0x02 +#define ROW_FLAG_EMPTY 0x04 +#define ROW_FLAG_SEPARATOR 0x08 + +class Property +{ + public: + Property(nsIAtom* aAtom) + : mAtom(aAtom), mNext(nsnull) { + } + ~Property() { + delete mNext; + } + + nsCOMPtr mAtom; + Property* mNext; +}; + +class Row +{ + public: + static Row* + Create(nsFixedSizeAllocator& aAllocator, + nsIContent* aContent, PRInt32 aParentIndex) { + void* place = aAllocator.Alloc(sizeof(Row)); + return place ? ::new(place) Row(aContent, aParentIndex) : nsnull; + } + + static void + Destroy(nsFixedSizeAllocator& aAllocator, Row* aRow) { + aRow->~Row(); + aAllocator.Free(aRow, sizeof(*aRow)); + } + + Row(nsIContent* aContent, PRInt32 aParentIndex) + : mContent(aContent), mParentIndex(aParentIndex), + mSubtreeSize(0), mProperty(nsnull), mFlags(0) { + } + + ~Row() { + delete mProperty; + } + + void SetContainer(PRBool aContainer) { + aContainer ? mFlags |= ROW_FLAG_CONTAINER : mFlags &= ~ROW_FLAG_CONTAINER; + } + PRBool IsContainer() { return mFlags & ROW_FLAG_CONTAINER; } + + void SetOpen(PRBool aOpen) { + aOpen ? mFlags |= ROW_FLAG_OPEN : mFlags &= ~ROW_FLAG_OPEN; + } + PRBool IsOpen() { return mFlags & ROW_FLAG_OPEN; } + + void SetEmpty(PRBool aEmpty) { + aEmpty ? mFlags |= ROW_FLAG_EMPTY : mFlags &= ~ROW_FLAG_EMPTY; + } + PRBool IsEmpty() { return mFlags & ROW_FLAG_EMPTY; } + + void SetSeparator(PRBool aSeparator) { + aSeparator ? mFlags |= ROW_FLAG_SEPARATOR : mFlags &= ~ROW_FLAG_SEPARATOR; + } + PRBool IsSeparator() { return mFlags & ROW_FLAG_SEPARATOR; } + + void SetProperty(Property* aProperty) { + delete mProperty; + mProperty = aProperty; + } + + // Weak reference to a content item. + nsIContent* mContent; + + // The parent index of the item, set to -1 for the top level items. + PRInt32 mParentIndex; + + // Subtree size for this item. + PRInt32 mSubtreeSize; + + // List of parsed properties + Property* mProperty; + + private: + // Hide so that only Create() and Destroy() can be used to + // allocate and deallocate from the heap + static void* operator new(size_t) { return 0; } + static void operator delete(void*, size_t) {} + + // State flags + PRInt8 mFlags; +}; + + +// We don't reference count the reference to the document +// If the document goes away first, we'll be informed and we +// can drop our reference. +// If we go away first, we'll get rid of ourselves from the +// document's observer list. + +nsOutlinerContentView::nsOutlinerContentView(void) : + mBoxObject(nsnull), mSelection(nsnull), mRoot(nsnull), mDocument(nsnull) +{ + NS_INIT_ISUPPORTS(); + + static const size_t kBucketSizes[] = { + sizeof(Row) + }; + static const PRInt32 kNumBuckets = sizeof(kBucketSizes) / sizeof(size_t); + static const PRInt32 kInitialSize = 16; + + mAllocator.Init("nsOutlinerContentView", kBucketSizes, kNumBuckets, kInitialSize); +} + +nsOutlinerContentView::~nsOutlinerContentView (void) +{ + // Remove ourselfs from document's observers. + if (mDocument) + mDocument->RemoveObserver(this); +} + +nsresult +NS_NewOutlinerContentView(nsIOutlinerContentView** aResult) +{ + *aResult = new nsOutlinerContentView; + if (! *aResult) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(*aResult); + return NS_OK; +} + + +NS_IMPL_ISUPPORTS3(nsOutlinerContentView, + nsIOutlinerView, + nsIOutlinerContentView, + nsIDocumentObserver); + + +NS_IMETHODIMP +nsOutlinerContentView::GetRowCount(PRInt32* aRowCount) +{ + *aRowCount = mRows.Count(); + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::GetSelection(nsIOutlinerSelection** aSelection) +{ + NS_IF_ADDREF(*aSelection = mSelection); + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::SetSelection(nsIOutlinerSelection* aSelection) +{ + mSelection = aSelection; + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::GetRowProperties(PRInt32 aIndex, nsISupportsArray* aProperties) +{ + NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad index"); + if (aIndex < 0 || aIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + Row* row = (Row*)mRows[aIndex]; + Property* property = row->mProperty; + while (property) { + aProperties->AppendElement(property->mAtom); + property = property->mNext; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::GetCellProperties(PRInt32 aRow, const PRUnichar* aColID, nsISupportsArray* aProperties) +{ + NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad row"); + if (aRow < 0 || aRow >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + Row* row = (Row*)mRows[aRow]; + nsCOMPtr realRow; + GetImmediateChild(row->mContent, nsXULAtoms::outlinerrow, getter_AddRefs(realRow)); + if (realRow) { + nsCOMPtr cell; + GetNamedCell(realRow, aColID, getter_AddRefs(cell)); + if (cell) { + nsAutoString properties; + cell->GetAttr(kNameSpaceID_None, nsXULAtoms::properties, properties); + if (properties.Length()) + nsOutlinerUtils::TokenizeProperties(properties, aProperties); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::GetColumnProperties(const PRUnichar* aColID, nsIDOMElement* aColElt, nsISupportsArray* aProperties) +{ + nsAutoString properties; + aColElt->GetAttribute(NS_LITERAL_STRING("properties"), properties); + if (properties.Length()) + nsOutlinerUtils::TokenizeProperties(properties, aProperties); + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::IsContainer(PRInt32 aIndex, PRBool* _retval) +{ + NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad index"); + if (aIndex < 0 || aIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + *_retval = ((Row*)mRows[aIndex])->IsContainer(); + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::IsContainerOpen(PRInt32 aIndex, PRBool* _retval) +{ + NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad index"); + if (aIndex < 0 || aIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + *_retval = ((Row*)mRows[aIndex])->IsOpen(); + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::IsContainerEmpty(PRInt32 aIndex, PRBool* _retval) +{ + NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad index"); + if (aIndex < 0 || aIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + *_retval = ((Row*)mRows[aIndex])->IsEmpty(); + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::IsSeparator(PRInt32 aIndex, PRBool *_retval) +{ + NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad index"); + if (aIndex < 0 || aIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + *_retval = ((Row*)mRows[aIndex])->IsSeparator(); + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::IsSorted(PRBool *_retval) +{ + *_retval = PR_FALSE; + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::CanDropOn(PRInt32 aIndex, PRBool *_retval) +{ + NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad index"); + if (aIndex < 0 || aIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + *_retval = PR_FALSE; + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::CanDropBeforeAfter(PRInt32 aIndex, PRBool aBefore, PRBool* _retval) +{ + NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad index"); + if (aIndex < 0 || aIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + *_retval = PR_FALSE; + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::Drop(PRInt32 aRow, PRInt32 aOrientation) +{ + NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad row"); + if (aRow < 0 || aRow >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::GetParentIndex(PRInt32 aRowIndex, PRInt32* _retval) +{ + NS_PRECONDITION(aRowIndex >= 0 && aRowIndex < mRows.Count(), "bad row index"); + if (aRowIndex < 0 || aRowIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + *_retval = ((Row*)mRows[aRowIndex])->mParentIndex; + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::HasNextSibling(PRInt32 aRowIndex, PRInt32 aAfterIndex, PRBool* _retval) +{ + NS_PRECONDITION(aRowIndex >= 0 && aRowIndex < mRows.Count(), "bad row index"); + if (aRowIndex < 0 || aRowIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + // We have a next sibling if the row is not the last in the subtree. + PRInt32 parentIndex = ((Row*)mRows[aRowIndex])->mParentIndex; + if (parentIndex >= 0) { + // Compute the last index in this subtree. + PRInt32 lastIndex = parentIndex + ((Row*)mRows[parentIndex])->mSubtreeSize; + Row* row = (Row*)mRows[lastIndex]; + while (row->mParentIndex != parentIndex) { + lastIndex = row->mParentIndex; + row = (Row*)mRows[lastIndex]; + } + + *_retval = aRowIndex < lastIndex; + } + else + *_retval = aRowIndex < mRows.Count() - 1; + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::GetLevel(PRInt32 aIndex, PRInt32* _retval) +{ + NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad index"); + if (aIndex < 0 || aIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + PRInt32 level = 0; + Row* row = (Row*)mRows[aIndex]; + while (row->mParentIndex >= 0) { + level++; + row = (Row*)mRows[row->mParentIndex]; + } + *_retval = level; + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::GetCellText(PRInt32 aRow, const PRUnichar* aColID, PRUnichar** _retval) +{ + NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad row"); + if (aRow < 0 || aRow >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + *_retval = nsnull; + + Row* row = (Row*)mRows[aRow]; + nsCOMPtr realRow; + GetImmediateChild(row->mContent, nsXULAtoms::outlinerrow, getter_AddRefs(realRow)); + if (realRow) { + nsCOMPtr cell; + GetNamedCell(realRow, aColID, getter_AddRefs(cell)); + if (cell) { + nsAutoString label; + cell->GetAttr(kNameSpaceID_None, nsHTMLAtoms::label, label); + *_retval = ToNewUnicode(label); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::SetOutliner(nsIOutlinerBoxObject* aOutliner) +{ + mBoxObject = aOutliner; + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::ToggleOpenState(PRInt32 aIndex) +{ + NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad index"); + if (aIndex < 0 || aIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + Row* row = (Row*)mRows[aIndex]; + if (row->IsOpen()) { + CloseContainer(aIndex); + row->mContent->UnsetAttr(kNameSpaceID_None, nsXULAtoms::open, PR_FALSE); + } + else { + OpenContainer(aIndex); + row->mContent->SetAttr(kNameSpaceID_None, nsXULAtoms::open, NS_LITERAL_STRING("true"), PR_FALSE); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::CycleHeader(const PRUnichar* aColID, nsIDOMElement *aColElt) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::SelectionChanged() +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::CycleCell(PRInt32 aRow, const PRUnichar* aColID) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::IsEditable(PRInt32 aRow, const PRUnichar* aColID, PRBool* _retval) +{ + *_retval = PR_FALSE; + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::SetCellText(PRInt32 aRow, const PRUnichar* aColID, const PRUnichar* aValue) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::PerformAction(const PRUnichar* aAction) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::PerformActionOnRow(const PRUnichar* aAction, PRInt32 aRow) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::PerformActionOnCell(const PRUnichar* aAction, PRInt32 aRowconst, const PRUnichar* aColID) +{ + return NS_OK; +} + + +NS_IMETHODIMP +nsOutlinerContentView::GetRoot(nsIDOMElement** aRoot) +{ + if (mRoot) + mRoot->QueryInterface(NS_GET_IID(nsIDOMElement), (void**)aRoot); + else + *aRoot = nsnull; + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::SetRoot(nsIDOMElement* aRoot) +{ + if (mRoot) { + // Remove ourselfs from document's observers. + if (mDocument) { + mDocument->RemoveObserver(this); + mDocument = nsnull; + } + + ClearRows(); + } + + if (aRoot) + mRoot = do_QueryInterface(aRoot); + + if (mRoot) { + // Add ourselfs to document's observers. + nsCOMPtr document; + mRoot->GetDocument(*getter_AddRefs(document)); + if (document) { + document->AddObserver(this); + mDocument = document; + } + + PRInt32 index = 0; + Serialize(mRoot, -1, &index, mRows); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::GetItemAtIndex(PRInt32 aIndex, nsIDOMElement** _retval) +{ + NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad index"); + if (aIndex < 0 || aIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + Row* row = (Row*)mRows[aIndex]; + row->mContent->QueryInterface(NS_GET_IID(nsIDOMElement), (void**)_retval); + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::GetIndexOfItem(nsIDOMElement* aItem, PRInt32* _retval) +{ + nsCOMPtr content = do_QueryInterface(aItem); + *_retval = FindContent(content); + + return NS_OK; +} + + +NS_IMETHODIMP +nsOutlinerContentView::BeginUpdate(nsIDocument *aDocument) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::EndUpdate(nsIDocument *aDocument) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::BeginLoad(nsIDocument *aDocument) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::EndLoad(nsIDocument *aDocument) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::BeginReflow(nsIDocument *aDocument, nsIPresShell* aShell) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::EndReflow(nsIDocument *aDocument, nsIPresShell* aShell) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::ContentChanged(nsIDocument *aDocument, + nsIContent* aContent, + nsISupports* aSubContent) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::ContentStatesChanged(nsIDocument* aDocument, + nsIContent* aContent1, + nsIContent* aContent2) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::AttributeChanged(nsIDocument *aDocument, + nsIContent* aContent, + PRInt32 aNameSpaceID, + nsIAtom* aAttribute, + PRInt32 aModType, + PRInt32 aHint) +{ + // First, get the element on which the attribute has changed + // and then try to find content item in our array of rows. + nsCOMPtr tag; + aContent->GetTag(*getter_AddRefs(tag)); + + if (tag == nsXULAtoms::outlinercol) { + if (aAttribute == nsXULAtoms::properties) + mBoxObject->Invalidate(); + } + else if (tag == nsXULAtoms::outlineritem) { + if (aAttribute == nsXULAtoms::open) { + PRInt32 index = FindContent(aContent); + if (index >= 0) { + Row* row = (Row*)mRows[index]; + nsAutoString open; + aContent->GetAttr(kNameSpaceID_None, nsXULAtoms::open, open); + PRBool isOpen = open.Equals(NS_LITERAL_STRING("true")); + PRBool wasOpen = row->IsOpen(); + if (! isOpen && wasOpen) + CloseContainer(index); + else if (isOpen && ! wasOpen) + OpenContainer(index); + } + } + } + else if (tag == nsXULAtoms::outlinerseparator) { + if (aAttribute == nsXULAtoms::properties) { + PRInt32 index = FindContent(aContent); + if (index >= 0) { + Row* row = (Row*)mRows[index]; + row->SetProperty(nsnull); + ParseProperties(aContent, &row->mProperty); + mBoxObject->InvalidateRow(index); + } + } + } + else if (tag == nsXULAtoms::outlinerrow) { + if (aAttribute == nsXULAtoms::properties) { + nsCOMPtr parent; + aContent->GetParent(*getter_AddRefs(parent)); + if (parent) { + PRInt32 index = FindContent(parent); + if (index >= 0) { + Row* row = (Row*)mRows[index]; + row->SetProperty(nsnull); + ParseProperties(aContent, &row->mProperty); + mBoxObject->InvalidateRow(index); + } + } + } + } + else if (tag == nsXULAtoms::outlinercell) { + if (aAttribute == nsXULAtoms::ref || + aAttribute == nsHTMLAtoms::label || + aAttribute == nsXULAtoms::properties) { + nsCOMPtr parent; + aContent->GetParent(*getter_AddRefs(parent)); + if (parent) { + nsCOMPtr grandParent; + parent->GetParent(*getter_AddRefs(grandParent)); + if (grandParent) { + PRInt32 index = FindContent(grandParent); + if (index >= 0) { + // XXX Should we make an effort to invalidate only cell ? + mBoxObject->InvalidateRow(index); + } + } + } + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::ContentAppended(nsIDocument *aDocument, + nsIContent* aContainer, + PRInt32 aNewIndexInContainer) +{ + nsCOMPtr child; + aContainer->ChildAt(aNewIndexInContainer, *getter_AddRefs(child)); + ContentInserted(aDocument, aContainer, child, aNewIndexInContainer); + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::ContentInserted(nsIDocument *aDocument, + nsIContent* aContainer, + nsIContent* aChild, + PRInt32 aIndexInContainer) +{ + nsCOMPtr childTag; + aChild->GetTag(*getter_AddRefs(childTag)); + + if (childTag == nsXULAtoms::outlineritem || + childTag == nsXULAtoms::outlinerseparator) { + PRInt32 parentIndex = -1; + if (aContainer != mRoot) { + nsCOMPtr parent; + aContainer->GetParent(*getter_AddRefs(parent)); + parentIndex = FindContent(parent); + } + + PRInt32 index = 0; + GetIndexInSubtree(aContainer, aChild, &index); + + PRInt32 count; + InsertRow(parentIndex, index, aChild, &count); + mBoxObject->RowCountChanged(parentIndex + index + 1, count); + } + else if (childTag == nsXULAtoms::outlinerchildren) { + PRInt32 index = FindContent(aContainer); + if (index >= 0) { + Row* row = (Row*)mRows[index]; + row->SetEmpty(PR_FALSE); + mBoxObject->InvalidateRow(index); + if (row->IsContainer() && row->IsOpen()) { + PRInt32 count; + EnsureSubtree(index, &count); + mBoxObject->RowCountChanged(index + 1, count); + } + } + } + else if (childTag == nsXULAtoms::outlinerrow) { + PRInt32 index = FindContent(aContainer); + if (index >= 0) + mBoxObject->InvalidateRow(index); + } + else if (childTag == nsXULAtoms::outlinercell) { + nsCOMPtr parent; + aContainer->GetParent(*getter_AddRefs(parent)); + if (parent) { + PRInt32 index = FindContent(parent); + if (index >= 0) + mBoxObject->InvalidateRow(index); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::ContentReplaced(nsIDocument *aDocument, + nsIContent* aContainer, + nsIContent* aOldChild, + nsIContent* aNewChild, + PRInt32 aIndexInContainer) +{ + ContentRemoved(aDocument, aContainer, aOldChild, aIndexInContainer); + ContentInserted(aDocument, aContainer, aNewChild, aIndexInContainer); + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::ContentRemoved(nsIDocument *aDocument, + nsIContent* aContainer, + nsIContent* aChild, + PRInt32 aIndexInContainer) +{ + nsCOMPtr tag; + aChild->GetTag(*getter_AddRefs(tag)); + + if (tag == nsXULAtoms::outlineritem || + tag == nsXULAtoms::outlinerseparator) { + PRInt32 index = FindContent(aChild); + if (index >= 0) { + PRInt32 count; + RemoveRow(index, &count); + mBoxObject->RowCountChanged(index, -count); + } + } + else if (tag == nsXULAtoms::outlinerchildren) { + PRInt32 index = FindContent(aContainer); + if (index >= 0) { + Row* row = (Row*)mRows[index]; + row->SetEmpty(PR_TRUE); + PRInt32 count; + RemoveSubtree(index, &count); + // Invalidate also the row to update twisty. + mBoxObject->InvalidateRow(index); + mBoxObject->RowCountChanged(index + 1, -count); + } + } + else if (tag == nsXULAtoms::outlinerrow) { + PRInt32 index = FindContent(aContainer); + if (index >= 0) + mBoxObject->InvalidateRow(index); + } + else if (tag == nsXULAtoms::outlinercell) { + nsCOMPtr parent; + aContainer->GetParent(*getter_AddRefs(parent)); + if (parent) { + PRInt32 index = FindContent(parent); + if (index >= 0) + mBoxObject->InvalidateRow(index); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::StyleSheetAdded(nsIDocument *aDocument, + nsIStyleSheet* aStyleSheet) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::StyleSheetRemoved(nsIDocument *aDocument, + nsIStyleSheet* aStyleSheet) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::StyleSheetDisabledStateChanged(nsIDocument *aDocument, + nsIStyleSheet* aStyleSheet, + PRBool aDisabled) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::StyleRuleChanged(nsIDocument *aDocument, + nsIStyleSheet* aStyleSheet, + nsIStyleRule* aStyleRule, + PRInt32 aHint) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::StyleRuleAdded(nsIDocument *aDocument, + nsIStyleSheet* aStyleSheet, + nsIStyleRule* aStyleRule) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::StyleRuleRemoved(nsIDocument *aDocument, + nsIStyleSheet* aStyleSheet, + nsIStyleRule* aStyleRule) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutlinerContentView::DocumentWillBeDestroyed(nsIDocument *aDocument) +{ + // Remove ourselfs from document's observers. + if (mDocument) { + mDocument->RemoveObserver(this); + mDocument = nsnull; + } + + ClearRows(); + + return NS_OK; +} + + +// Recursively serialize content, starting with aContent. +void +nsOutlinerContentView::Serialize(nsIContent* aContent, PRInt32 aParentIndex, PRInt32* aIndex, nsVoidArray& aRows) +{ + PRInt32 childCount; + aContent->ChildCount(childCount); + for (PRInt32 i = 0; i < childCount; i++) { + nsCOMPtr content; + aContent->ChildAt(i, *getter_AddRefs(content)); + nsCOMPtr tag; + content->GetTag(*getter_AddRefs(tag)); + PRInt32 count = aRows.Count(); + if (tag == nsXULAtoms::outlineritem) + SerializeItem(content, aParentIndex, aIndex, aRows); + else if (tag == nsXULAtoms::outlinerseparator) + SerializeSeparator(content, aParentIndex, aIndex, aRows); + *aIndex += aRows.Count() - count; + } +} + +void +nsOutlinerContentView::SerializeItem(nsIContent* aContent, PRInt32 aParentIndex, PRInt32* aIndex, nsVoidArray& aRows) +{ + Row* row = Row::Create(mAllocator, aContent, aParentIndex); + aRows.AppendElement(row); + + nsCOMPtr realRow; + GetImmediateChild(aContent, nsXULAtoms::outlinerrow, getter_AddRefs(realRow)); + if (realRow) + ParseProperties(realRow, &row->mProperty); + + nsAutoString container; + aContent->GetAttr(kNameSpaceID_None, nsXULAtoms::container, container); + if (container.Equals(NS_LITERAL_STRING("true"))) { + row->SetContainer(PR_TRUE); + nsAutoString open; + aContent->GetAttr(kNameSpaceID_None, nsXULAtoms::open, open); + if (open.Equals(NS_LITERAL_STRING("true"))) { + row->SetOpen(PR_TRUE); + nsCOMPtr child; + GetImmediateChild(aContent, nsXULAtoms::outlinerchildren, getter_AddRefs(child)); + if (child) { + // Now, recursively serialize our child. + PRInt32 count = aRows.Count(); + PRInt32 index = 0; + Serialize(child, aParentIndex + *aIndex + 1, &index, aRows); + row->mSubtreeSize += aRows.Count() - count; + } + else + row->SetEmpty(PR_TRUE); + } + } +} + +void +nsOutlinerContentView::SerializeSeparator(nsIContent* aContent, PRInt32 aParentIndex, PRInt32* aIndex, nsVoidArray& aRows) +{ + Row* row = Row::Create(mAllocator, aContent, aParentIndex); + row->SetSeparator(PR_TRUE); + aRows.AppendElement(row); + + ParseProperties(aContent, &row->mProperty); +} + +void +nsOutlinerContentView::GetIndexInSubtree(nsIContent* aContainer, nsIContent* aContent, PRInt32* aIndex) +{ + PRInt32 childCount; + aContainer->ChildCount(childCount); + for (PRInt32 i = 0; i < childCount; i++) { + nsCOMPtr content; + aContainer->ChildAt(i, *getter_AddRefs(content)); + if (content == aContent) + break; + + nsCOMPtr tag; + content->GetTag(*getter_AddRefs(tag)); + if (tag == nsXULAtoms::outlineritem) { + (*aIndex)++; + nsAutoString container; + content->GetAttr(kNameSpaceID_None, nsXULAtoms::container, container); + if (container.Equals(NS_LITERAL_STRING("true"))) { + nsAutoString open; + content->GetAttr(kNameSpaceID_None, nsXULAtoms::open, open); + if (open.Equals(NS_LITERAL_STRING("true"))) { + nsCOMPtr child; + GetImmediateChild(content, nsXULAtoms::outlinerchildren, getter_AddRefs(child)); + if (child) + GetIndexInSubtree(child, aContent, aIndex); + } + } + } + else if (tag == nsXULAtoms::outlinerseparator) + (*aIndex)++; + } +} + +void +nsOutlinerContentView::EnsureSubtree(PRInt32 aIndex, PRInt32* aCount) +{ + Row* row = (Row*)mRows[aIndex]; + nsCOMPtr child; + GetImmediateChild(row->mContent, nsXULAtoms::outlinerchildren, getter_AddRefs(child)); + if (! child) { + *aCount = 0; + return; + } + + nsAutoVoidArray rows; + PRInt32 index = 0; + Serialize(child, aIndex, &index, rows); + mRows.InsertElementsAt(rows, aIndex + 1); + PRInt32 count = rows.Count(); + + row->mSubtreeSize += count; + UpdateSubtreeSizes(row->mParentIndex, count); + + // Update parent indexes, but skip newly added rows. + // They already have correct values. + UpdateParentIndexes(aIndex, count, count); + + *aCount = count; +} + +void +nsOutlinerContentView::RemoveSubtree(PRInt32 aIndex, PRInt32* aCount) +{ + Row* row = (Row*)mRows[aIndex]; + PRInt32 count = row->mSubtreeSize; + + for(PRInt32 i = 0; i < count; i++) { + Row* nextRow = (Row*)mRows[aIndex + i + 1]; + Row::Destroy(mAllocator, nextRow); + } + mRows.RemoveElementsAt(aIndex + 1, count); + + row->mSubtreeSize -= count; + UpdateSubtreeSizes(row->mParentIndex, -count); + + UpdateParentIndexes(aIndex, 0, -count); + + *aCount = count; +} + +void +nsOutlinerContentView::InsertRow(PRInt32 aParentIndex, PRInt32 aIndex, nsIContent* aContent, PRInt32* aCount) +{ + nsAutoVoidArray rows; + nsCOMPtr tag; + aContent->GetTag(*getter_AddRefs(tag)); + if (tag == nsXULAtoms::outlineritem) + SerializeItem(aContent, aParentIndex, &aIndex, rows); + else if (tag == nsXULAtoms::outlinerseparator) + SerializeSeparator(aContent, aParentIndex, &aIndex, rows); + mRows.InsertElementsAt(rows, aParentIndex + aIndex + 1); + PRInt32 count = rows.Count(); + + UpdateSubtreeSizes(aParentIndex, count); + + // Update parent indexes, but skip added rows. + // They already have correct values. + UpdateParentIndexes(aParentIndex + aIndex, count + 1, count); + + *aCount = count; +} + +void +nsOutlinerContentView::RemoveRow(PRInt32 aIndex, PRInt32* aCount) +{ + Row* row = (Row*)mRows[aIndex]; + PRInt32 count = row->mSubtreeSize + 1; + PRInt32 parentIndex = row->mParentIndex; + + Row::Destroy(mAllocator, row); + for(PRInt32 i = 1; i < count; i++) { + Row* nextRow = (Row*)mRows[aIndex + i]; + Row::Destroy(mAllocator, nextRow); + } + mRows.RemoveElementsAt(aIndex, count); + + UpdateSubtreeSizes(parentIndex, -count); + + UpdateParentIndexes(aIndex, 0, -count); + + *aCount = count; +} + +void +nsOutlinerContentView::ClearRows() +{ + for (PRInt32 i = 0; i < mRows.Count(); i++) + Row::Destroy(mAllocator, (Row*)mRows[i]); + mRows.Clear(); + mRoot = nsnull; +} + +void +nsOutlinerContentView::OpenContainer(PRInt32 aIndex) +{ + Row* row = (Row*)mRows[aIndex]; + row->SetOpen(PR_TRUE); + + PRInt32 count; + EnsureSubtree(aIndex, &count); + mBoxObject->RowCountChanged(aIndex + 1, count); +} + + +void +nsOutlinerContentView::CloseContainer(PRInt32 aIndex) +{ + Row* row = (Row*)mRows[aIndex]; + row->SetOpen(PR_FALSE); + + PRInt32 count; + RemoveSubtree(aIndex, &count); + mBoxObject->RowCountChanged(aIndex + 1, -count); +} + +PRInt32 +nsOutlinerContentView::FindContent(nsIContent* aContent) +{ + for (PRInt32 i = 0; i < mRows.Count(); i++) + if (((Row*)mRows[i])->mContent == aContent) + return i; + + return -1; +} + +void +nsOutlinerContentView::UpdateSubtreeSizes(PRInt32 aParentIndex, PRInt32 count) +{ + while (aParentIndex >= 0) { + Row* row = (Row*)mRows[aParentIndex]; + row->mSubtreeSize += count; + aParentIndex = row->mParentIndex; + } +} + +void +nsOutlinerContentView::UpdateParentIndexes(PRInt32 aIndex, PRInt32 aSkip, PRInt32 aCount) +{ + PRInt32 count = mRows.Count(); + for (PRInt32 i = aIndex + aSkip; i < count; i++) { + Row* row = (Row*)mRows[i]; + if (row->mParentIndex > aIndex) + row->mParentIndex += aCount; + } +} + +nsresult +nsOutlinerContentView::GetImmediateChild(nsIContent* aContainer, nsIAtom* aTag, nsIContent** aResult) +{ + PRInt32 childCount; + aContainer->ChildCount(childCount); + for (PRInt32 i = 0; i < childCount; i++) { + nsCOMPtr child; + aContainer->ChildAt(i, *getter_AddRefs(child)); + nsCOMPtr tag; + child->GetTag(*getter_AddRefs(tag)); + if (tag == aTag) { + NS_ADDREF(*aResult = child); + return NS_OK; + } + } + + *aResult = nsnull; + return NS_OK; +} + +nsresult +nsOutlinerContentView::GetColIndex(const PRUnichar* aColID, PRInt32* aResult) +{ + *aResult = -1; + + // First, try to find col index by already set "colIndex" attribute. + nsCOMPtr domDocument = do_QueryInterface(mDocument); + nsCOMPtr domElement; + domDocument->GetElementById(nsDependentString(aColID), getter_AddRefs(domElement)); + if (domElement) { + nsAutoString colIndexValue; + domElement->GetAttribute(NS_LITERAL_STRING("colIndex"), colIndexValue); + if (colIndexValue.Length()) { + PRInt32 rv; + *aResult = colIndexValue.ToInteger(&rv); + } + } + + if (*aResult == -1) { + // No "colIndex" attribute, traverse through cols + nsCOMPtr parent; + mRoot->GetParent(*getter_AddRefs(parent)); + if (parent) { + PRInt32 childCount; + parent->ChildCount(childCount); + PRInt32 j = 0; + for (PRInt32 i = 0; i < childCount; i++) { + nsCOMPtr child; + parent->ChildAt(i, *getter_AddRefs(child)); + nsCOMPtr tag; + child->GetTag(*getter_AddRefs(tag)); + if (tag == nsXULAtoms::outlinercol) { + nsAutoString id; + child->GetAttr(kNameSpaceID_None, nsHTMLAtoms::id, id); + if (id.Equals(aColID)) { + // Found it, set "colIndex" attribute to speed up next call. + nsCOMPtr colIndexAtom; + colIndexAtom = dont_AddRef(NS_NewAtom(NS_LITERAL_STRING("colIndex"))); + nsAutoString colIndexValue; + colIndexValue.AppendInt(j); + child->SetAttr(kNameSpaceID_None, colIndexAtom, colIndexValue, PR_FALSE); + *aResult = j; + break; + } + j++; + } + } + } + } + + return NS_OK; +} + +nsresult +nsOutlinerContentView::GetNamedCell(nsIContent* aContainer, const PRUnichar* aColID, nsIContent** aResult) +{ + PRInt32 colIndex; + GetColIndex(aColID, &colIndex); + + // Traverse through cells, try to find the cell by "ref" attribute or by cell + // index in a row. "ref" attribute has higher priority. + *aResult = nsnull; + PRInt32 childCount; + aContainer->ChildCount(childCount); + PRInt32 j = 0; + for (PRInt32 i = 0; i < childCount; i++) { + nsCOMPtr cell; + aContainer->ChildAt(i, *getter_AddRefs(cell)); + nsCOMPtr tag; + cell->GetTag(*getter_AddRefs(tag)); + if (tag == nsXULAtoms::outlinercell) { + nsAutoString ref; + cell->GetAttr(kNameSpaceID_None, nsXULAtoms::ref, ref); + if (ref.Equals(aColID)) { + *aResult = cell; + break; + } + else if (j == colIndex) + *aResult = cell; + j++; + } + } + NS_IF_ADDREF(*aResult); + + return NS_OK; +} + +nsresult +nsOutlinerContentView::ParseProperties(nsIContent* aContent, Property** aProperty) +{ + nsAutoString properties; + aContent->GetAttr(kNameSpaceID_None, nsXULAtoms::properties, properties); + if (properties.Length()) { + Property* lastProperty = *aProperty; + + nsAString::const_iterator end; + properties.EndReading(end); + + nsAString::const_iterator iter; + properties.BeginReading(iter); + + do { + // Skip whitespace + while (iter != end && nsCRT::IsAsciiSpace(*iter)) + ++iter; + + // If only whitespace, we're done + if (iter == end) + break; + + // Note the first non-whitespace character + nsAString::const_iterator first = iter; + + // Advance to the next whitespace character + while (iter != end && ! nsCRT::IsAsciiSpace(*iter)) + ++iter; + + nsCOMPtr atom = dont_AddRef(NS_NewAtom(Substring(first, iter))); + Property* newProperty = new Property(atom); + if (lastProperty) + lastProperty->mNext = newProperty; + else + *aProperty = newProperty; + lastProperty = newProperty; + + } while (iter != end); + } + + return NS_OK; +} diff --git a/layout/xul/base/src/tree/src/nsTreeContentView.h b/layout/xul/base/src/tree/src/nsTreeContentView.h new file mode 100644 index 000000000000..bbad1611f5dc --- /dev/null +++ b/layout/xul/base/src/tree/src/nsTreeContentView.h @@ -0,0 +1,194 @@ +/* -*- 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): + * Jan Varga (varga@utcru.sk) + * + * 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 ***** */ + +#ifndef nsOutlinerContentView_h__ +#define nsOutlinerContentView_h__ + +#include "nsCOMPtr.h" +#include "nsFixedSizeAllocator.h" +#include "nsVoidArray.h" +#include "nsIContent.h" +#include "nsIDocument.h" +#include "nsIDocumentObserver.h" +#include "nsIOutlinerView.h" +#include "nsIOutlinerBoxObject.h" +#include "nsIOutlinerSelection.h" +#include "nsIOutlinerContentView.h" + +class Property; + +class nsOutlinerContentView : public nsIOutlinerView, + public nsIOutlinerContentView, + public nsIDocumentObserver +{ + public: + nsOutlinerContentView(void); + + virtual ~nsOutlinerContentView(void); + + friend nsresult NS_NewOutlinerContentView(nsIOutlinerContentView** aResult); + + NS_DECL_ISUPPORTS + + NS_DECL_NSIOUTLINERVIEW + + NS_DECL_NSIOUTLINERCONTENTVIEW + + // nsIDocumentObserver + NS_IMETHOD BeginUpdate(nsIDocument *aDocument); + + NS_IMETHOD EndUpdate(nsIDocument *aDocument); + + NS_IMETHOD BeginLoad(nsIDocument *aDocument); + + NS_IMETHOD EndLoad(nsIDocument *aDocument); + + NS_IMETHOD BeginReflow(nsIDocument *aDocument, nsIPresShell* aShell); + + NS_IMETHOD EndReflow(nsIDocument *aDocument, nsIPresShell* aShell); + + NS_IMETHOD ContentChanged(nsIDocument *aDocument, + nsIContent* aContent, + nsISupports* aSubContent); + + NS_IMETHOD ContentStatesChanged(nsIDocument* aDocument, + nsIContent* aContent1, + nsIContent* aContent2); + + NS_IMETHOD AttributeChanged(nsIDocument *aDocument, + nsIContent* aContent, + PRInt32 aNameSpaceID, + nsIAtom* aAttribute, + PRInt32 aModType, + PRInt32 aHint); + + NS_IMETHOD ContentAppended(nsIDocument *aDocument, + nsIContent* aContainer, + PRInt32 aNewIndexInContainer); + + NS_IMETHOD ContentInserted(nsIDocument *aDocument, + nsIContent* aContainer, + nsIContent* aChild, + PRInt32 aIndexInContainer); + + NS_IMETHOD ContentReplaced(nsIDocument *aDocument, + nsIContent* aContainer, + nsIContent* aOldChild, + nsIContent* aNewChild, + PRInt32 aIndexInContainer); + + NS_IMETHOD ContentRemoved(nsIDocument *aDocument, + nsIContent* aContainer, + nsIContent* aChild, + PRInt32 aIndexInContainer); + + NS_IMETHOD StyleSheetAdded(nsIDocument *aDocument, + nsIStyleSheet* aStyleSheet); + + NS_IMETHOD StyleSheetRemoved(nsIDocument *aDocument, + nsIStyleSheet* aStyleSheet); + + NS_IMETHOD StyleSheetDisabledStateChanged(nsIDocument *aDocument, + nsIStyleSheet* aStyleSheet, + PRBool aDisabled); + + NS_IMETHOD StyleRuleChanged(nsIDocument *aDocument, + nsIStyleSheet* aStyleSheet, + nsIStyleRule* aStyleRule, + PRInt32 aHint); + + NS_IMETHOD StyleRuleAdded(nsIDocument *aDocument, + nsIStyleSheet* aStyleSheet, + nsIStyleRule* aStyleRule); + + NS_IMETHOD StyleRuleRemoved(nsIDocument *aDocument, + nsIStyleSheet* aStyleSheet, + nsIStyleRule* aStyleRule); + + NS_IMETHOD DocumentWillBeDestroyed(nsIDocument *aDocument); + + protected: + // Recursive methods which deal with serializing of nested content. + void Serialize(nsIContent* aContent, PRInt32 aParentIndex, PRInt32* aIndex, nsVoidArray& aRows); + + void SerializeItem(nsIContent* aContent, PRInt32 aParentIndex, PRInt32* aIndex, nsVoidArray& aRows); + + void SerializeSeparator(nsIContent* aContent, PRInt32 aParentIndex, PRInt32* aIndex, nsVoidArray& aRows); + + void GetIndexInSubtree(nsIContent* aContainer, nsIContent* aContent, PRInt32* aResult); + + // Helper methods which we use to manage our plain array of rows. + void EnsureSubtree(PRInt32 aIndex, PRInt32* aCount); + + void RemoveSubtree(PRInt32 aIndex, PRInt32* aCount); + + void InsertRow(PRInt32 aParentIndex, PRInt32 aIndex, nsIContent* aContent, PRInt32* aCount); + + void RemoveRow(PRInt32 aIndex, PRInt32* aCount); + + void ClearRows(); + + void OpenContainer(PRInt32 aIndex); + + void CloseContainer(PRInt32 aIndex); + + PRInt32 FindContent(nsIContent* aContent); + + void UpdateSubtreeSizes(PRInt32 aIndex, PRInt32 aCount); + + void UpdateParentIndexes(PRInt32 aIndex, PRInt32 aSkip, PRInt32 aCount); + + // Content helpers. + nsresult GetImmediateChild(nsIContent* aContainer, nsIAtom* aTag, nsIContent** aResult); + + nsresult GetColIndex(const PRUnichar* aColID, PRInt32* aResult); + + nsresult GetNamedCell(nsIContent* aContainer, const PRUnichar* aColID, nsIContent** aResult); + + nsresult ParseProperties(nsIContent* aContent, Property** aProperty); + + private: + nsCOMPtr mBoxObject; + nsCOMPtr mSelection; + nsCOMPtr mRoot; + nsIDocument* mDocument; // WEAK + nsFixedSizeAllocator mAllocator; + nsVoidArray mRows; +}; + +#endif // nsOutlinerContentView_h__ diff --git a/layout/xul/base/src/tree/src/nsTreeUtils.cpp b/layout/xul/base/src/tree/src/nsTreeUtils.cpp new file mode 100644 index 000000000000..0a05d503abb8 --- /dev/null +++ b/layout/xul/base/src/tree/src/nsTreeUtils.cpp @@ -0,0 +1,80 @@ +/* -*- 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): + * Chris Waterson + * + * 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 "nsOutlinerUtils.h" + +nsresult +nsOutlinerUtils::TokenizeProperties(const nsAString& aProperties, nsISupportsArray* aPropertiesArray) +{ + NS_PRECONDITION(aPropertiesArray != nsnull, "null ptr"); + if (! aPropertiesArray) + return NS_ERROR_NULL_POINTER; + + nsAString::const_iterator end; + aProperties.EndReading(end); + + nsAString::const_iterator iter; + aProperties.BeginReading(iter); + + do { + // Skip whitespace + while (iter != end && nsCRT::IsAsciiSpace(*iter)) + ++iter; + + // If only whitespace, we're done + if (iter == end) + break; + + // Note the first non-whitespace character + nsAString::const_iterator first = iter; + + // Advance to the next whitespace character + while (iter != end && ! nsCRT::IsAsciiSpace(*iter)) + ++iter; + + // XXX this would be nonsensical + NS_ASSERTION(iter != first, "eh? something's wrong here"); + if (iter == first) + break; + + nsCOMPtr atom = dont_AddRef(NS_NewAtom(Substring(first, iter))); + aPropertiesArray->AppendElement(atom); + } while (iter != end); + + return NS_OK; +} diff --git a/layout/xul/base/src/tree/src/nsTreeUtils.h b/layout/xul/base/src/tree/src/nsTreeUtils.h new file mode 100644 index 000000000000..a657193d06db --- /dev/null +++ b/layout/xul/base/src/tree/src/nsTreeUtils.h @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 3; 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): + * Chris Waterson + * + * 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 ***** */ + +#ifndef nsOutlinerUtils_h__ +#define nsOutlinerUtils_h__ + +#include "nsString.h" +#include "nsISupportsArray.h" + +class nsOutlinerUtils +{ + public: + /** + * Parse a whitespace separated list of properties into an array + * of atoms. + */ + static nsresult + TokenizeProperties(const nsAString& aProperties, nsISupportsArray* aPropertiesArray); +}; + +#endif // nsOutlinerUtils_h__