bug 335122, rework XUL sort service, r=jan, sr=sicking

This commit is contained in:
enndeakin%sympatico.ca 2006-07-13 15:08:55 +00:00
parent a679017eb1
commit 1f3797eb6a
14 changed files with 1024 additions and 1596 deletions

View File

@ -36,57 +36,30 @@
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
#include "nsIRDFCompositeDataSource.idl"
#include "nsIRDFResource.idl"
%{C++
class nsRDFSortState;
%}
[ptr] native nsRDFSortState(nsRDFSortState);
interface nsIContent;
interface nsIDOMNode;
/**
* A service used to sort the contents of a XUL widget.
*/
[scriptable, uuid(BFD05261-834C-11d2-8EAC-00805F29F371)]
[scriptable, uuid(F29270C8-3BE5-4046-9B57-945A84DFF132)]
interface nsIXULSortService : nsISupports
{
/**
*
* Sort the contents of the widget containing <code>aNode</code>
* using <code>aSortResource</code> as the comparison key, and
* using <code>aSortKey</code> as the comparison key, and
* <code>aSortDirection</code> as the direction.
*
* @param aNode A node in the XUL widget whose children are to be
* sorted. <code>sort</code> will traverse upwards to find the
* root node at which to begin the actualy sorting. For optimal
* results, pass in the root of the widget.
*
* @param aSortResource The RDF resource to be used as
* the comparison key.
*
* @param aNode A node in the XUL widget whose children are to be sorted.
* @param aSortKey The value to be used as the comparison key.
* @param aSortDirection May be either <b>natural</b> to return
* the contents to their natural (unsorted) order,
* <b>ascending</b> to sort the contents in ascending order, or
* <b>descending</b> to sort the contents in descending order.
*/
void sort(in nsIDOMNode aNode,
in AString aSortResource,
in AString aSortKey,
in AString aSortDirection);
/**
* Used internally for insertion sorting.
*/
[noscript] void insertContainerNode(in nsIRDFCompositeDataSource db,
in nsRDFSortState sortStatePtr,
in nsIContent root,
in nsIContent trueParent,
in nsIContent container,
in nsIContent node,
in boolean aNotify);
};
%{C++

View File

@ -159,7 +159,7 @@ interface nsIXULTemplateQueryProcessor;
*
* See http://wiki.mozilla.org/XUL:Templates_Plan for details about templates.
*/
[scriptable, uuid(e025ef24-d508-11d9-9633-cd2d4a843228)]
[scriptable, uuid(fd8fe8a1-5dc3-4830-84b7-f75baccb4a9b)]
interface nsIXULTemplateBuilder : nsISupports
{
/**
@ -181,6 +181,11 @@ interface nsIXULTemplateBuilder : nsISupports
*/
readonly attribute nsIXULTemplateResult rootResult;
/**
* The query processor used to generate results.
*/
[noscript] readonly attribute nsIXULTemplateQueryProcessor queryProcessor;
/**
* Force the template builder to rebuild its content. All existing content
* will be removed first. The query processor's done() method will be
@ -269,6 +274,14 @@ interface nsIXULTemplateBuilder : nsISupports
*/
nsIXULTemplateResult getResultForId(in AString aId);
/**
* Retrieve the result corresponding to a generated element, or null is
* there isn't one.
*
* @param aContent element to result the result of
*/
nsIXULTemplateResult getResultForContent(in nsIDOMElement aElement);
/**
* Returns true if the node has content generated for it. This method is
* intended to be called only by the RDF query processor. If aTag is set,

View File

@ -250,6 +250,10 @@ interface nsIXULTemplateQueryProcessor : nsISupports
* The comparison should only consider the values for the specified
* variable.
*
* If the comparison variable is null, the results may be
* sorted in a natural order, for instance, based on the order the data in
* stored in the datasource.
*
* This method must only be called with results that were created by this
* query processor.
*

View File

@ -55,7 +55,7 @@ interface nsIRDFResource;
* particular variable may be retrieved using the getBindingFor and
* getBindingObjectFor methods.
*/
[scriptable, uuid(d035f34f-d8ee-444a-bd46-41163f54ed25)]
[scriptable, uuid(ebea0230-36fa-41b7-8e31-760806057965)]
interface nsIXULTemplateResult : nsISupports
{
/**
@ -92,6 +92,13 @@ interface nsIXULTemplateResult : nsISupports
*/
readonly attribute nsIRDFResource resource;
/**
* The type of the object. The predefined value 'separator' may be used
* for separators. Other values may be used for application specific
* purposes.
*/
readonly attribute AString type;
/**
* Get the string representation of the value of a variable for this
* result. This string will be used in the action body from a template as

View File

@ -1,71 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla 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/MPL/
*
* 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):
*
* Alternatively, the contents of this file may be used under the terms of
* either of 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 MPL, 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 MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/*
Shared structure for sort state.
*/
#ifndef nsRDFSort_h__
#define nsRDFSort_h__
#include "nsIRDFResource.h"
#include "nsIRDFDataSource.h"
#include "nsIContent.h"
#include "nsString.h"
#include "prtypes.h"
class nsRDFSortState
{
public:
// state match strings
nsAutoString sortResource, sortResource2;
// state variables
nsCOMPtr<nsIRDFDataSource> mCache;
nsCOMPtr<nsIRDFResource> sortProperty, sortProperty2;
nsCOMPtr<nsIRDFResource> sortPropertyColl, sortPropertyColl2;
nsCOMPtr<nsIRDFResource> sortPropertySort, sortPropertySort2;
nsCOMPtr<nsIContent> lastContainer;
PRBool lastWasFirst, lastWasLast;
};
#endif // nsRDFSort_h__

View File

@ -48,12 +48,11 @@
#include "nsIServiceManager.h"
#include "nsITextContent.h"
#include "nsIXULDocument.h"
#include "nsIXULSortService.h"
#include "nsContentSupportMap.h"
#include "nsRDFConMemberTestNode.h"
#include "nsRDFPropertyTestNode.h"
#include "nsRDFSort.h"
#include "nsXULSortService.h"
#include "nsTemplateRule.h"
#include "nsTemplateMap.h"
#include "nsVoidArray.h"
@ -74,10 +73,6 @@
#include "pldhash.h"
#include "rdf.h"
//----------------------------------------------------------------------0
static NS_DEFINE_CID(kXULSortServiceCID, NS_XULSORTSERVICE_CID);
//----------------------------------------------------------------------
//
// Return values for EnsureElementHasGenericChild()
@ -141,6 +136,9 @@ public:
nsIAtom* aTag,
PRBool* aGenerated);
NS_IMETHOD GetResultForContent(nsIDOMElement* aContent,
nsIXULTemplateResult** aResult);
// nsIDocumentObserver interface
virtual void AttributeChanged(nsIDocument* aDocument,
nsIContent* aContent,
@ -155,8 +153,6 @@ protected:
NS_NewXULContentBuilder(nsISupports* aOuter, REFNSIID aIID, void** aResult);
nsXULContentBuilder();
virtual ~nsXULContentBuilder();
nsresult InitGlobals();
virtual void Uninit(PRBool aIsFinal);
@ -413,6 +409,26 @@ protected:
virtual nsresult
SynchronizeResult(nsIXULTemplateResult* aResult);
/**
* Compare a result to a content node. If the generated content for the
* result should come before aContent, set aSortOrder to -1. If it should
* come after, set sortOrder to 1. If both are equal, set to 0.
*/
nsresult
CompareResultToNode(nsIXULTemplateResult* aResult, nsIContent* aContent,
PRInt32* aSortOrder);
/**
* Insert a generated node into the container where it should go according
* to the current sort. aNode is the generated content node and aResult is
* the result for the generated node.
*/
nsresult
InsertSortedNode(nsIContent* aContainer,
nsIContent* aNode,
nsIXULTemplateResult* aResult,
PRBool aNotify);
/**
* Maintains a mapping between elements in the DOM and the matches
* that they support.
@ -428,16 +444,9 @@ protected:
/**
* Information about the currently active sort
*/
nsRDFSortState sortState;
// pseudo-constants
static PRInt32 gRefCnt;
static nsIXULSortService* gXULSortService;
nsSortState mSortState;
};
PRInt32 nsXULContentBuilder::gRefCnt;
nsIXULSortService* nsXULContentBuilder::gXULSortService;
NS_IMETHODIMP
NS_NewXULContentBuilder(nsISupports* aOuter, REFNSIID aIID, void** aResult)
{
@ -463,25 +472,7 @@ NS_NewXULContentBuilder(nsISupports* aOuter, REFNSIID aIID, void** aResult)
nsXULContentBuilder::nsXULContentBuilder()
{
}
nsXULContentBuilder::~nsXULContentBuilder()
{
if (--gRefCnt == 0) {
NS_IF_RELEASE(gXULSortService);
}
}
nsresult
nsXULContentBuilder::InitGlobals()
{
if (gRefCnt++ == 0) {
nsresult rv = CallGetService(kXULSortServiceCID, &gXULSortService);
if (NS_FAILED(rv))
return rv;
}
return nsXULTemplateBuilder::InitGlobals();
mSortState.initialized = PR_FALSE;
}
void
@ -497,6 +488,8 @@ nsXULContentBuilder::Uninit(PRBool aIsFinal)
mContentSupportMap.Clear();
mTemplateMap.Clear();
mSortState.initialized = PR_FALSE;
nsXULTemplateBuilder::Uninit(aIsFinal);
}
@ -868,12 +861,8 @@ nsXULContentBuilder::BuildContentFromTemplate(nsIContent *aTemplateNode,
if (! isUnique) {
rv = NS_ERROR_UNEXPECTED;
if (gXULSortService && isGenerationElement) {
rv = gXULSortService->InsertContainerNode(mCompDB, &sortState,
mRoot, aResourceNode,
aRealNode, realKid,
aNotify);
}
if (isGenerationElement)
rv = InsertSortedNode(aRealNode, realKid, aChild, aNotify);
if (NS_FAILED(rv)) {
rv = aRealNode->AppendChildTo(realKid, aNotify);
@ -1740,6 +1729,29 @@ nsXULContentBuilder::HasGeneratedContent(nsIRDFResource* aResource,
return NS_OK;
}
NS_IMETHODIMP
nsXULContentBuilder::GetResultForContent(nsIDOMElement* aElement,
nsIXULTemplateResult** aResult)
{
NS_ENSURE_ARG_POINTER(aElement);
NS_ENSURE_ARG_POINTER(aResult);
nsCOMPtr<nsIContent> content = do_QueryInterface(aElement);
if (content == mRoot) {
*aResult = mRootResult;
}
else {
nsTemplateMatch *match = nsnull;
if (mContentSupportMap.Get(content, &match))
*aResult = match->mResult;
else
*aResult = nsnull;
}
NS_IF_ADDREF(*aResult);
return NS_OK;
}
//----------------------------------------------------------------------
//
// nsIDocumentObserver methods
@ -1765,6 +1777,13 @@ nsXULContentBuilder::AttributeChanged(nsIDocument* aDocument,
CloseContainer(aContent);
}
if ((aNameSpaceID == kNameSpaceID_XUL) &&
((aAttribute == nsXULAtoms::sort) ||
(aAttribute == nsXULAtoms::sortDirection) ||
(aAttribute == nsXULAtoms::sortResource) ||
(aAttribute == nsXULAtoms::sortResource2)))
mSortState.initialized = PR_FALSE;
// Pass along to the generic template builder.
nsXULTemplateBuilder::AttributeChanged(aDocument, aContent, aNameSpaceID,
aAttribute, aModType);
@ -1785,7 +1804,6 @@ nsXULContentBuilder::NodeWillBeDestroyed(const nsINode* aNode)
// nsXULTemplateBuilder methods
//
PRBool
nsXULContentBuilder::GetInsertionLocations(nsIXULTemplateResult* aResult,
nsISupportsArray** aLocations)
@ -2077,3 +2095,202 @@ nsXULContentBuilder::RebuildAll()
return NS_OK;
}
/**** Sorting Methods ****/
nsresult
nsXULContentBuilder::CompareResultToNode(nsIXULTemplateResult* aResult,
nsIContent* aContent,
PRInt32* aSortOrder)
{
NS_ASSERTION(aSortOrder, "CompareResultToNode: null out param aSortOrder");
*aSortOrder = 0;
nsTemplateMatch *match = nsnull;
if (!mContentSupportMap.Get(aContent, &match)) {
*aSortOrder = mSortState.sortStaticsLast ? -1 : 1;
return NS_OK;
}
if (!mQueryProcessor)
return NS_OK;
if (mSortState.direction == nsSortState_natural) {
// sort in natural order
nsresult rv = mQueryProcessor->CompareResults(aResult, match->mResult,
nsnull, aSortOrder);
NS_ENSURE_SUCCESS(rv, rv);
}
else {
// iterate over each sort key and compare. If the nodes are equal,
// continue to compare using the next sort key. If not equal, stop.
PRInt32 length = mSortState.sortKeys.Count();
for (PRInt32 t = 0; t < length; t++) {
nsresult rv = mQueryProcessor->CompareResults(aResult, match->mResult,
mSortState.sortKeys[t], aSortOrder);
NS_ENSURE_SUCCESS(rv, rv);
if (*aSortOrder)
break;
}
}
// flip the sort order if performing a descending sorting
if (mSortState.direction == nsSortState_descending)
*aSortOrder = -*aSortOrder;
return NS_OK;
}
nsresult
nsXULContentBuilder::InsertSortedNode(nsIContent* aContainer,
nsIContent* aNode,
nsIXULTemplateResult* aResult,
PRBool aNotify)
{
nsresult rv;
if (!mSortState.initialized) {
nsAutoString sort, sortDirection;
mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::sort, sort);
mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::sortDirection, sortDirection);
rv = XULSortServiceImpl::InitializeSortState(mRoot, aContainer,
sort, sortDirection, &mSortState);
NS_ENSURE_SUCCESS(rv, rv);
}
// when doing a natural sort, items will typically be sorted according to
// the order they appear in the datasource. For RDF, cache whether the
// reference parent is an RDF Seq. That way, the items can be sorted in the
// order they are in the Seq.
mSortState.isContainerRDFSeq = PR_FALSE;
if (mSortState.direction == nsSortState_natural) {
nsCOMPtr<nsISupports> ref;
nsresult rv = aResult->GetBindingObjectFor(mRefVariable, getter_AddRefs(ref));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIRDFResource> container = do_QueryInterface(ref);
if (container) {
rv = gRDFContainerUtils->IsSeq(mDB, container, &mSortState.isContainerRDFSeq);
NS_ENSURE_SUCCESS(rv, rv);
}
}
PRBool childAdded = PR_FALSE;
PRUint32 numChildren = aContainer->GetChildCount();
if (mSortState.direction != nsSortState_natural ||
(mSortState.direction == nsSortState_natural && mSortState.isContainerRDFSeq))
{
// because numChildren gets modified
PRInt32 realNumChildren = numChildren;
nsIContent *child = nsnull;
// rjc says: determine where static XUL ends and generated XUL/RDF begins
PRInt32 staticCount = 0;
nsAutoString staticValue;
aContainer->GetAttr(kNameSpaceID_None, nsXULAtoms::staticHint, staticValue);
if (!staticValue.IsEmpty())
{
// found "static" XUL element count hint
PRInt32 strErr = 0;
staticCount = staticValue.ToInteger(&strErr);
if (strErr)
staticCount = 0;
} else {
// compute the "static" XUL element count
for (PRUint32 childLoop = 0; childLoop < numChildren; ++childLoop) {
child = aContainer->GetChildAt(childLoop);
if (nsContentUtils::HasNonEmptyAttr(child, kNameSpaceID_None,
nsXULAtoms::_template))
break;
else
++staticCount;
}
if (mSortState.sortStaticsLast) {
// indicate that static XUL comes after RDF-generated content by
// making negative
staticCount = -staticCount;
}
// save the "static" XUL element count hint
nsAutoString valueStr;
valueStr.AppendInt(staticCount);
aContainer->SetAttr(kNameSpaceID_None, nsXULAtoms::staticHint, valueStr, PR_FALSE);
}
if (staticCount <= 0) {
numChildren += staticCount;
staticCount = 0;
} else if (staticCount > (PRInt32)numChildren) {
staticCount = numChildren;
numChildren -= staticCount;
}
// figure out where to insert the node when a sort order is being imposed
if (numChildren > 0) {
nsIContent *temp;
PRInt32 direction;
// rjc says: The following is an implementation of a fairly optimal
// binary search insertion sort... with interpolation at either end-point.
if (mSortState.lastWasFirst) {
child = aContainer->GetChildAt(staticCount);
temp = child;
rv = CompareResultToNode(aResult, temp, &direction);
if (direction < 0) {
aContainer->InsertChildAt(aNode, staticCount, aNotify);
childAdded = PR_TRUE;
} else
mSortState.lastWasFirst = PR_FALSE;
} else if (mSortState.lastWasLast) {
child = aContainer->GetChildAt(realNumChildren - 1);
temp = child;
rv = CompareResultToNode(aResult, temp, &direction);
if (direction > 0) {
aContainer->InsertChildAt(aNode, realNumChildren, aNotify);
childAdded = PR_TRUE;
} else
mSortState.lastWasLast = PR_FALSE;
}
PRInt32 left = staticCount + 1, right = realNumChildren, x;
while (!childAdded && right >= left) {
x = (left + right) / 2;
child = aContainer->GetChildAt(x - 1);
temp = child;
rv = CompareResultToNode(aResult, temp, &direction);
if ((x == left && direction < 0) ||
(x == right && direction >= 0) ||
left == right)
{
PRInt32 thePos = (direction > 0 ? x : x - 1);
aContainer->InsertChildAt(aNode, thePos, aNotify);
childAdded = PR_TRUE;
mSortState.lastWasFirst = (thePos == staticCount);
mSortState.lastWasLast = (thePos >= realNumChildren);
break;
}
if (direction < 0)
right = x - 1;
else
left = x + 1;
}
}
}
// if the child hasn't been inserted yet, just add it at the end. Note
// that an append isn't done as there may be static content afterwards.
if (!childAdded)
aContainer->InsertChildAt(aNode, numChildren, aNotify);
return NS_OK;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,199 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla 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/MPL/
*
* 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):
* Scott Putterman <putterman@netscape.com>
* Pierre Phaneuf <pp@ludusdesign.com>
* Chase Tingley <tingley@sundell.net>
* Neil Deakin <enndeakin@sympatico.ca>
*
* Alternatively, the contents of this file may be used under the terms of
* either of 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 MPL, 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 MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK *****
*
* This Original Code has been modified by IBM Corporation.
* Modifications made by IBM described herein are
* Copyright (c) International Business Machines
* Corporation, 2000
*
* Modifications to Mozilla code or documentation
* identified per MPL Section 3.3
*
* Date Modified by Description of modification
* 03/27/2000 IBM Corp. Added PR_CALLBACK for Optlink
* use in OS2
*/
/*
This sort service is used to sort template built content or content by attribute.
*/
#include "nsCOMPtr.h"
#include "nsCOMArray.h"
#include "nsTArray.h"
#include "nsIContent.h"
#include "nsIXULTemplateResult.h"
#include "nsIXULTemplateQueryProcessor.h"
#include "nsIXULSortService.h"
enum nsSortState_direction {
nsSortState_descending,
nsSortState_ascending,
nsSortState_natural
};
// the sort state holds info about the current sort
struct nsSortState
{
PRBool initialized;
PRBool invertSort;
PRBool inbetweenSeparatorSort;
PRBool sortStaticsLast;
PRBool isContainerRDFSeq;
nsSortState_direction direction;
nsAutoString sort;
nsCOMArray<nsIAtom> sortKeys;
nsCOMPtr<nsIXULTemplateQueryProcessor> processor;
nsCOMPtr<nsIContent> lastContainer;
PRBool lastWasFirst, lastWasLast;
nsSortState()
: initialized(PR_FALSE)
{
}
};
// information about a particular item to be sorted
struct contentSortInfo {
nsCOMPtr<nsIContent> content;
nsCOMPtr<nsIContent> parent;
nsCOMPtr<nsIXULTemplateResult> result;
contentSortInfo(nsIContent* aContent,
nsIXULTemplateResult* aResult)
: content(aContent), result(aResult)
{}
};
////////////////////////////////////////////////////////////////////////
// ServiceImpl
//
// This is the sort service.
//
class XULSortServiceImpl : public nsIXULSortService
{
protected:
XULSortServiceImpl(void) {};
virtual ~XULSortServiceImpl(void) {};
friend nsresult NS_NewXULSortService(nsIXULSortService** mgr);
private:
public:
// nsISupports
NS_DECL_ISUPPORTS
// nsISortService
NS_DECL_NSIXULSORTSERVICE
/**
* Set sort and sortDirection attributes when a sort is done.
*/
void
SetSortHints(nsIContent *aNode, nsSortState* aSortState);
/**
* Set sortActive and sortDirection attributes on a tree column when a sort
* is done. The column to change is the one with a sort attribute that
* matches the sort key. The sort attributes are removed from the other
* columns.
*/
void
SetSortColumnHints(nsIContent *content,
const nsAString &sortResource,
const nsAString &sortDirection);
/**
* Determine the list of items which need to be sorted. This is determined
* in the following way:
* - for elements that have a content builder, get its list of generated
* results
* - otherwise, for trees, get the child treeitems
* - otherwise, get the direct children
*/
nsresult
GetItemsToSort(nsIContent *aContainer,
nsSortState* aSortState,
nsTArray<contentSortInfo *>& aSortItems);
/**
* Get the list of items to sort for template built content
*/
nsresult
GetTemplateItemsToSort(nsIContent* aContainer,
nsIXULTemplateBuilder* aBuilder,
nsSortState* aSortState,
nsTArray<contentSortInfo *>& aSortItems);
/**
* Sort a container using the supplied sort state details.
*/
nsresult
SortContainer(nsIContent *aContainer, nsSortState* aSortState);
/**
* Given a list of sortable items, reverse the list. This is done
* when simply changing the sort direction for the same key.
*/
nsresult
InvertSortInfo(nsTArray<contentSortInfo *>* aData,
PRInt32 aStart, PRInt32 aNumItems);
/**
* Initialize sort information from attributes specified on the container,
* the sort key and sort direction.
*
* @param aRootElement the element that contains sort attributes
* @param aContainer the container to sort, usually equal to aRootElement
* @param aSortKey space separated list of sort keys
* @param aSortDirection direction to sort in
* @param aSortState structure filled in with sort data
*/
static nsresult
InitializeSortState(nsIContent* aRootElement,
nsIContent* aContainer,
const nsAString& aSortKey,
const nsAString& aSortDirection,
nsSortState* aSortState);
};

View File

@ -260,6 +260,13 @@ nsXULTemplateBuilder::GetDatabase(nsIRDFCompositeDataSource** aResult)
return NS_OK;
}
NS_IMETHODIMP
nsXULTemplateBuilder::GetQueryProcessor(nsIXULTemplateQueryProcessor** aResult)
{
NS_IF_ADDREF(*aResult = mQueryProcessor.get());
return NS_OK;
}
NS_IMETHODIMP
nsXULTemplateBuilder::AddRuleFilter(nsIDOMNode* aRule, nsIXULTemplateRuleFilter* aFilter)
{
@ -834,6 +841,14 @@ nsXULTemplateBuilder::GetResultForId(const nsAString& aId,
return NS_OK;
}
NS_IMETHODIMP
nsXULTemplateBuilder::GetResultForContent(nsIDOMElement* aContent,
nsIXULTemplateResult** aResult)
{
*aResult = nsnull;
return NS_OK;
}
NS_IMETHODIMP
nsXULTemplateBuilder::AddListener(nsIXULBuilderListener* aListener)
{

View File

@ -55,6 +55,7 @@
#include "nsIXULDocument.h"
#include "nsUnicharUtils.h"
#include "nsAttrName.h"
#include "rdf.h"
#include "nsContentTestNode.h"
#include "nsRDFConInstanceTestNode.h"
@ -78,7 +79,8 @@ static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
nsrefcnt nsXULTemplateQueryProcessorRDF::gRefCnt = 0;
nsIRDFService* nsXULTemplateQueryProcessorRDF::gRDFService;
nsIRDFContainerUtils* nsXULTemplateQueryProcessorRDF::gRDFContainerUtils;
nsIRDFResource* nsXULTemplateQueryProcessorRDF::kNC_BookmarkSeparator;
nsIRDFResource* nsXULTemplateQueryProcessorRDF::kRDF_type;
NS_IMPL_ISUPPORTS2(nsXULTemplateQueryProcessorRDF,
nsIXULTemplateQueryProcessor,
@ -100,6 +102,8 @@ nsXULTemplateQueryProcessorRDF::~nsXULTemplateQueryProcessorRDF(void)
if (--gRefCnt == 0) {
NS_IF_RELEASE(gRDFService);
NS_IF_RELEASE(gRDFContainerUtils);
NS_IF_RELEASE(kNC_BookmarkSeparator);
NS_IF_RELEASE(kRDF_type);
}
}
@ -122,6 +126,18 @@ nsXULTemplateQueryProcessorRDF::InitGlobals()
return rv;
}
if (!kNC_BookmarkSeparator) {
gRDFService->GetResource(
NS_LITERAL_CSTRING(NC_NAMESPACE_URI "BookmarkSeparator"),
&kNC_BookmarkSeparator);
}
if (!kRDF_type) {
gRDFService->GetResource(
NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "type"),
&kRDF_type);
}
return NS_OK;
}
@ -190,6 +206,7 @@ nsXULTemplateQueryProcessorRDF::Done()
mDB = nsnull;
mBuilder = nsnull;
mRefVariable = nsnull;
mLastRef = nsnull;
mGenerationStarted = PR_FALSE;
@ -228,8 +245,10 @@ nsXULTemplateQueryProcessorRDF::CompileQuery(nsIXULTemplateBuilder* aBuilder,
return NS_ERROR_OUT_OF_MEMORY;
query->mRefVariable = aRefVariable;
if (!mRefVariable)
mRefVariable = aRefVariable;
if (! aMemberVariable)
if (!aMemberVariable)
query->mMemberVariable = do_GetAtom("?");
else
query->mMemberVariable = aMemberVariable;
@ -428,7 +447,8 @@ nsXULTemplateQueryProcessorRDF::AddBinding(nsIDOMNode* aRuleNode,
return bindings->AddBinding(aVar, aRef, property);
}
NS_IMETHODIMP nsXULTemplateQueryProcessorRDF::TranslateRef(nsISupports* aDatasource,
NS_IMETHODIMP
nsXULTemplateQueryProcessorRDF::TranslateRef(nsISupports* aDatasource,
const nsAString& aRefString,
nsIXULTemplateResult** aRef)
{
@ -450,18 +470,63 @@ NS_IMETHODIMP nsXULTemplateQueryProcessorRDF::TranslateRef(nsISupports* aDatasou
return NS_OK;
}
NS_IMETHODIMP nsXULTemplateQueryProcessorRDF::CompareResults(nsIXULTemplateResult* aLeft,
NS_IMETHODIMP
nsXULTemplateQueryProcessorRDF::CompareResults(nsIXULTemplateResult* aLeft,
nsIXULTemplateResult* aRight,
nsIAtom* aVar,
PRInt32* aResult)
{
NS_ENSURE_ARG_POINTER(aLeft);
NS_ENSURE_ARG_POINTER(aRight);
*aResult = 0;
nsCOMPtr<nsISupports> leftNode;
aLeft->GetBindingObjectFor(aVar, getter_AddRefs(leftNode));
// for natural order sorting, use the index in the RDF container for the
// order. If there is no container, just sort them arbitrarily.
if (!aVar) {
// if a result has a negative index, just sort it first
PRInt32 leftindex = GetContainerIndexOf(aLeft);
PRInt32 rightindex = GetContainerIndexOf(aRight);
*aResult = leftindex == rightindex ? 0 :
leftindex > rightindex ? 1 :
-1;
return NS_OK;
}
nsCOMPtr<nsISupports> rightNode;
nsAutoString sortkey;
aVar->ToString(sortkey);
nsCOMPtr<nsISupports> leftNode, rightNode;
if (!sortkey.IsEmpty() && sortkey[0] != '?' &&
!StringBeginsWith(sortkey, NS_LITERAL_STRING("rdf:")) &&
mDB) {
// if the sort key is not a template variable, it should be an RDF
// predicate. Get the targets and compare those instead.
nsCOMPtr<nsIRDFResource> predicate;
nsresult rv = gRDFService->GetUnicodeResource(sortkey, getter_AddRefs(predicate));
NS_ENSURE_SUCCESS(rv, rv);
// create a predicate with '?sort=true' appended. This special
// predicate may be used to have a different sort value than the
// displayed value
sortkey.AppendLiteral("?sort=true");
nsCOMPtr<nsIRDFResource> sortPredicate;
rv = gRDFService->GetUnicodeResource(sortkey, getter_AddRefs(sortPredicate));
NS_ENSURE_SUCCESS(rv, rv);
rv = GetSortValue(aLeft, predicate, sortPredicate, getter_AddRefs(leftNode));
NS_ENSURE_SUCCESS(rv, rv);
rv = GetSortValue(aRight, predicate, sortPredicate, getter_AddRefs(rightNode));
NS_ENSURE_SUCCESS(rv, rv);
}
else {
// get the values for the sort key from the results
aLeft->GetBindingObjectFor(aVar, getter_AddRefs(leftNode));
aRight->GetBindingObjectFor(aVar, getter_AddRefs(rightNode));
}
{
// Literals?
@ -546,6 +611,7 @@ NS_IMETHODIMP nsXULTemplateQueryProcessorRDF::CompareResults(nsIXULTemplateResul
}
}
// if the results are none of the above, just pretend that they are equal
return NS_OK;
}
@ -940,6 +1006,13 @@ nsXULTemplateQueryProcessorRDF::CheckEmpty(nsIRDFResource* aResource,
return NS_OK;
}
nsresult
nsXULTemplateQueryProcessorRDF::CheckIsSeparator(nsIRDFResource* aResource,
PRBool* aIsSeparator)
{
return mDB->HasAssertion(aResource, kRDF_type, kNC_BookmarkSeparator,
PR_TRUE, aIsSeparator);
}
//----------------------------------------------------------------------
@ -1640,3 +1713,68 @@ nsXULTemplateQueryProcessorRDF::RetractElement(const MemoryElement& aMemoryEleme
mMemoryElementToResultMap.Remove(hash);
}
}
PRInt32
nsXULTemplateQueryProcessorRDF::GetContainerIndexOf(nsIXULTemplateResult* aResult)
{
// get the reference variable and look up the container in the result
nsCOMPtr<nsISupports> ref;
nsresult rv = aResult->GetBindingObjectFor(mRefVariable,
getter_AddRefs(ref));
if (NS_FAILED(rv))
return -1;
nsCOMPtr<nsIRDFResource> container = do_QueryInterface(ref);
if (container) {
// if the container is an RDF Seq, return the index of the result
// in the container.
PRBool isSequence = PR_FALSE;
gRDFContainerUtils->IsSeq(mDB, container, &isSequence);
if (isSequence) {
nsCOMPtr<nsIRDFResource> resource;
aResult->GetResource(getter_AddRefs(resource));
if (resource) {
PRInt32 index;
gRDFContainerUtils->IndexOf(mDB, container, resource, &index);
return index;
}
}
}
// if the container isn't a Seq, or the result isn't in the container,
// return -1 indicating no index.
return -1;
}
nsresult
nsXULTemplateQueryProcessorRDF::GetSortValue(nsIXULTemplateResult* aResult,
nsIRDFResource* aPredicate,
nsIRDFResource* aSortPredicate,
nsISupports** aResultNode)
{
nsCOMPtr<nsIRDFResource> source;
nsresult rv = aResult->GetResource(getter_AddRefs(source));
if (NS_FAILED(rv))
return rv;
nsCOMPtr<nsIRDFNode> value;
if (source) {
// first check predicate?sort=true so that datasources may use a
// custom value for sorting
rv = mDB->GetTarget(source, aSortPredicate, PR_TRUE,
getter_AddRefs(value));
if (NS_FAILED(rv))
return rv;
if (!value) {
rv = mDB->GetTarget(source, aPredicate, PR_TRUE,
getter_AddRefs(value));
if (NS_FAILED(rv))
return rv;
}
}
*aResultNode = value;
NS_IF_ADDREF(*aResultNode);
return NS_OK;
}

View File

@ -140,6 +140,12 @@ public:
CheckEmpty(nsIRDFResource* aTargetResource,
PRBool* aIsEmpty);
/**
* Check if a resource is a separator
*/
nsresult
CheckIsSeparator(nsIRDFResource* aResource, PRBool* aIsSeparator);
/*
* Compute the containment properties which are additional arcs which
* indicate that a node is a container, in additional to the RDF container
@ -266,6 +272,23 @@ public:
*/
void RetractElement(const MemoryElement& aMemoryElement);
/**
* Return the index of a result's resource in its RDF container
*/
PRInt32
GetContainerIndexOf(nsIXULTemplateResult* aResult);
/**
* Given a result and a predicate to sort on, get the target value of
* the triple to use for sorting. The sort predicate is the predicate
* with '?sort=true' appended.
*/
nsresult
GetSortValue(nsIXULTemplateResult* aResult,
nsIRDFResource* aPredicate,
nsIRDFResource* aSortPredicate,
nsISupports** aResultNode);
nsIRDFDataSource* GetDataSource() { return mDB; }
nsIXULTemplateBuilder* GetBuilder() { return mBuilder; }
@ -315,6 +338,9 @@ protected:
// fixed size allocator used to allocate rule network structures
nsFixedSizeAllocator mPool;
// the reference variable
nsCOMPtr<nsIAtom> mRefVariable;
// the last ref that was calculated, used for simple rules
nsCOMPtr<nsIXULTemplateResult> mLastRef;
@ -364,6 +390,8 @@ protected:
public:
static nsIRDFService* gRDFService;
static nsIRDFContainerUtils* gRDFContainerUtils;
static nsIRDFResource* kNC_BookmarkSeparator;
static nsIRDFResource* kRDF_type;
nsFixedSizeAllocator& GetPool() { return mPool; }
};

View File

@ -119,6 +119,24 @@ nsXULTemplateResultRDF::GetResource(nsIRDFResource** aResource)
return NS_OK;
}
NS_IMETHODIMP
nsXULTemplateResultRDF::GetType(nsAString& aType)
{
aType.Truncate();
nsresult rv = NS_OK;
nsXULTemplateQueryProcessorRDF* processor = GetProcessor();
if (processor) {
PRBool found;
rv = processor->CheckIsSeparator(mNode, &found);
if (NS_SUCCEEDED(rv) && found)
aType.AssignLiteral("separator");
}
return rv;
}
NS_IMETHODIMP
nsXULTemplateResultRDF::GetBindingFor(nsIAtom* aVar, nsAString& aValue)
{

View File

@ -95,13 +95,6 @@ protected:
NS_NewXULTreeBuilder(nsISupports* aOuter, REFNSIID aIID, void** aResult);
nsXULTreeBuilder();
virtual ~nsXULTreeBuilder();
/**
* Initialize the template builder
*/
nsresult
InitGlobals();
/**
* Uninitialize the template builder
@ -274,15 +267,7 @@ protected:
* The builder observers.
*/
nsCOMPtr<nsISupportsArray> mObservers;
// pseudo-constants
static PRInt32 gRefCnt;
static nsIRDFResource* kRDF_type;
static nsIRDFResource* kNC_BookmarkSeparator;
};
PRInt32 nsXULTreeBuilder::gRefCnt = 0;
nsIRDFResource* nsXULTreeBuilder::kRDF_type;
nsIRDFResource* nsXULTreeBuilder::kNC_BookmarkSeparator;
//----------------------------------------------------------------------
@ -327,33 +312,6 @@ nsXULTreeBuilder::nsXULTreeBuilder()
{
}
nsresult
nsXULTreeBuilder::InitGlobals()
{
nsresult rv = nsXULTemplateBuilder::InitGlobals();
if (NS_FAILED(rv))
return rv;
if (gRefCnt++ == 0) {
gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "type"), &kRDF_type);
gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "BookmarkSeparator"),
&kNC_BookmarkSeparator);
}
return rv;
}
nsXULTreeBuilder::~nsXULTreeBuilder()
{
if (--gRefCnt == 0) {
NS_IF_RELEASE(kRDF_type);
NS_IF_RELEASE(kNC_BookmarkSeparator);
}
Uninit(PR_TRUE);
}
void
nsXULTreeBuilder::Uninit(PRBool aIsFinal)
{
@ -632,10 +590,11 @@ nsXULTreeBuilder::IsSeparator(PRInt32 aIndex, PRBool* aResult)
if (aIndex < 0 || aIndex >= mRows.Count())
return NS_ERROR_INVALID_ARG;
nsCOMPtr<nsIRDFResource> resource;
GetResourceFor(aIndex, getter_AddRefs(resource));
nsAutoString type;
nsTreeRows::Row& row = *(mRows[aIndex]);
row.mMatch->mResult->GetType(type);
mDB->HasAssertion(resource, kRDF_type, kNC_BookmarkSeparator, PR_TRUE, aResult);
*aResult = type.EqualsLiteral("separator");
return NS_OK;
}

View File

@ -47,6 +47,7 @@
#include "nsIDOMClassInfo.h"
#include "nsIEventStateManager.h"
#include "nsINodeInfo.h"
#include "nsIXULSortService.h"
// A content model view implementation for the tree.
@ -597,6 +598,37 @@ nsTreeContentView::ToggleOpenState(PRInt32 aIndex)
NS_IMETHODIMP
nsTreeContentView::CycleHeader(nsITreeColumn* aCol)
{
NS_ENSURE_ARG_POINTER(aCol);
if (!mRoot)
return NS_OK;
nsCOMPtr<nsIDOMElement> element;
aCol->GetElement(getter_AddRefs(element));
if (element) {
nsCOMPtr<nsIContent> column = do_QueryInterface(element);
nsAutoString sort;
column->GetAttr(kNameSpaceID_None, nsXULAtoms::sort, sort);
if (!sort.IsEmpty()) {
nsCOMPtr<nsIXULSortService> xs = do_GetService("@mozilla.org/xul/xul-sort-service;1");
if (xs) {
nsAutoString sortdirection;
static nsIContent::AttrValuesArray strings[] =
{&nsXULAtoms::ascending, &nsXULAtoms::descending, nsnull};
switch (column->FindAttrValueIn(kNameSpaceID_None,
nsXULAtoms::sortDirection,
strings, eCaseMatters)) {
case 0: sortdirection.AssignLiteral("descending"); break;
case 1: sortdirection.AssignLiteral("natural"); break;
default: sortdirection.AssignLiteral("ascending"); break;
}
nsCOMPtr<nsIDOMNode> rootnode = do_QueryInterface(mRoot);
xs->Sort(rootnode, sort, sortdirection);
}
}
}
return NS_OK;
}