fix for bug #358784: implement Firefox 2.0 style "View | By Date" and "View | By Date and Site" for places based history sidebar (GROUP_BY_DAY)
r=g#,brettw git-svn-id: svn://10.0.0.236/trunk@215591 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
parent
63363e3091
commit
d13f814777
@ -114,12 +114,12 @@ function SetPlace(aSearchString)
|
||||
placeURI += "&sort=" + NHQO.SORT_BY_DATE_DESCENDING;
|
||||
break;
|
||||
case "dayandsite":
|
||||
/* placeURI += "&group=" + NHQO.GROUP_BY_DAY; */
|
||||
placeURI += "&group=" + NHQO.GROUP_BY_DAY;
|
||||
placeURI += "&group=" + NHQO.GROUP_BY_HOST;
|
||||
placeURI += "&sort=" + NHQO.SORT_BY_TITLE_ASCENDING;
|
||||
break;
|
||||
default: /* "day" */
|
||||
/* placeURI += "&group=" + NHQO.GROUP_BY_DAY; */
|
||||
placeURI += "&group=" + NHQO.GROUP_BY_DAY;
|
||||
placeURI += "&sort=" + NHQO.SORT_BY_TITLE_ASCENDING;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -83,6 +83,7 @@ interface nsINavHistoryResultNode : nsISupports
|
||||
const PRUint32 RESULT_TYPE_QUERY = 5; // nsINavHistoryQueryResultNode
|
||||
const PRUint32 RESULT_TYPE_FOLDER = 6; // nsINavHistoryFolderResultNode
|
||||
const PRUint32 RESULT_TYPE_SEPARATOR = 7; // nsINavHistoryResultNode
|
||||
const PRUint32 RESULT_TYPE_DAY = 8; // nsINavHistoryContainerResultNode
|
||||
readonly attribute PRUint32 type;
|
||||
|
||||
/**
|
||||
@ -933,6 +934,13 @@ interface nsINavHistoryQuery : nsISupports
|
||||
[scriptable, uuid(25fd4de4-33b0-475e-a63d-2bcb1d123e0d)]
|
||||
interface nsINavHistoryQueryOptions : nsISupports
|
||||
{
|
||||
/**
|
||||
* Grouping by day. The results will be an array of nsINavHistoryResults with
|
||||
* type = RESULT_TYPE_DAY, one for each day where there are results. These
|
||||
* will have children of corresponding to the search results of that day.
|
||||
*/
|
||||
const PRUint32 GROUP_BY_DAY = 0;
|
||||
|
||||
/**
|
||||
* Groping by exact host. The results will be an array of nsINavHistoryResults
|
||||
* with type = RESULT_TYPE_HOST, one for each unique host (for example,
|
||||
@ -1003,8 +1011,11 @@ interface nsINavHistoryQueryOptions : nsISupports
|
||||
/**
|
||||
* The grouping mode to be used for this query.
|
||||
* Grouping mode is an array of GROUP_BY_* values that specifies the
|
||||
* structure of the tree you want. If you don't want grouping, you can
|
||||
* specify an empty array.
|
||||
* structure of the tree you want. For example, an array consisting of
|
||||
* [GROUP_BY_DAY, GROUP_BY_DOMAIN] will give you a tree whose first level is
|
||||
* a list of days, and whose second level is a list of domains, and whose
|
||||
* third level is a list of pages in those domains.
|
||||
* If you don't want grouping, you can specify an empty array.
|
||||
*/
|
||||
void getGroupingMode(out PRUint32 groupCount,
|
||||
[retval,array,size_is(groupCount)] out PRUint32 groupingMode);
|
||||
|
||||
@ -1154,6 +1154,21 @@ void nsNavHistory::expireNowTimerCallback(nsITimer* aTimer, void* aClosure)
|
||||
history->mExpireNowTimer = nsnull;
|
||||
}
|
||||
|
||||
static PRTime
|
||||
NormalizeTimeRelativeToday(PRTime aTime)
|
||||
{
|
||||
// round to midnight this morning
|
||||
PRExplodedTime explodedTime;
|
||||
PR_ExplodeTime(aTime, PR_LocalTimeParameters, &explodedTime);
|
||||
|
||||
// set to midnight (0:00)
|
||||
explodedTime.tm_min =
|
||||
explodedTime.tm_hour =
|
||||
explodedTime.tm_sec =
|
||||
explodedTime.tm_usec = 0;
|
||||
|
||||
return PR_ImplodeTime(&explodedTime);
|
||||
}
|
||||
|
||||
// nsNavHistory::NormalizeTime
|
||||
//
|
||||
@ -1173,17 +1188,9 @@ nsNavHistory::NormalizeTime(PRUint32 aRelative, PRTime aOffset)
|
||||
{
|
||||
case nsINavHistoryQuery::TIME_RELATIVE_EPOCH:
|
||||
return aOffset;
|
||||
case nsINavHistoryQuery::TIME_RELATIVE_TODAY: {
|
||||
// round to midnight this morning
|
||||
PRExplodedTime explodedTime;
|
||||
PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &explodedTime);
|
||||
explodedTime.tm_min =
|
||||
explodedTime.tm_hour =
|
||||
explodedTime.tm_sec =
|
||||
explodedTime.tm_usec = 0;
|
||||
ref = PR_ImplodeTime(&explodedTime);
|
||||
case nsINavHistoryQuery::TIME_RELATIVE_TODAY:
|
||||
ref = NormalizeTimeRelativeToday(PR_Now());
|
||||
break;
|
||||
}
|
||||
case nsINavHistoryQuery::TIME_RELATIVE_NOW:
|
||||
ref = PR_Now();
|
||||
break;
|
||||
@ -1194,7 +1201,6 @@ nsNavHistory::NormalizeTime(PRUint32 aRelative, PRTime aOffset)
|
||||
return ref + aOffset;
|
||||
}
|
||||
|
||||
|
||||
// nsNavHistory::CanLiveUpdateQuery
|
||||
//
|
||||
// Returns true if this set of queries/options can be live-updated. That is,
|
||||
@ -3254,6 +3260,9 @@ nsNavHistory::RecursiveGroup(const nsCOMArray<nsNavHistoryResultNode>& aSource,
|
||||
|
||||
nsresult rv;
|
||||
switch (aGroupingMode[0]) {
|
||||
case nsINavHistoryQueryOptions::GROUP_BY_DAY:
|
||||
rv = GroupByDay(aSource, aDest);
|
||||
break;
|
||||
case nsINavHistoryQueryOptions::GROUP_BY_HOST:
|
||||
rv = GroupByHost(aSource, aDest, PR_FALSE);
|
||||
break;
|
||||
@ -3284,6 +3293,92 @@ nsNavHistory::RecursiveGroup(const nsCOMArray<nsNavHistoryResultNode>& aSource,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// code borrowed from mozilla/xpfe/components/history/src/nsGlobalHistory.cpp
|
||||
// pass in a pre-normalized now and a date, and we'll find
|
||||
// the difference since midnight on each of the days.
|
||||
//
|
||||
// USECS_PER_DAY == PR_USEC_PER_SEC * 60 * 60 * 24;
|
||||
static const PRInt64 USECS_PER_DAY = LL_INIT(20, 500654080);
|
||||
static PRInt64
|
||||
GetAgeInDays(PRTime aNormalizedNow, PRTime aDate)
|
||||
{
|
||||
PRTime dateMidnight = NormalizeTimeRelativeToday(aDate);
|
||||
return ((aNormalizedNow - dateMidnight) / USECS_PER_DAY);
|
||||
}
|
||||
|
||||
// XXX todo
|
||||
// we should make "group by date" more flexible and extensible, in order
|
||||
// to allow extension developers to write better history sidebars and viewers
|
||||
// see bug #359346
|
||||
// nsNavHistory::GroupByDay
|
||||
nsresult
|
||||
nsNavHistory::GroupByDay(const nsCOMArray<nsNavHistoryResultNode>& aSource,
|
||||
nsCOMArray<nsNavHistoryResultNode>* aDest)
|
||||
{
|
||||
// 8 == today, yesterday, 2 ago, 3 ago, 4 ago, 5 ago, 6 ago, older than 6
|
||||
const PRInt32 numDays = 8;
|
||||
|
||||
nsNavHistoryContainerResultNode *dates[numDays];
|
||||
for (PRInt32 i = 0; i < numDays; i++)
|
||||
dates[i] = nsnull;
|
||||
|
||||
nsCAutoString dateNames[numDays];
|
||||
// special case: Today
|
||||
GetStringFromName(NS_LITERAL_STRING("finduri-AgeInDays-is-0").get(),
|
||||
dateNames[0]);
|
||||
// special case: Yesterday
|
||||
GetStringFromName(NS_LITERAL_STRING("finduri-AgeInDays-is-1").get(),
|
||||
dateNames[1]);
|
||||
for (PRInt32 curDay = 2; curDay <= numDays-2; curDay++) {
|
||||
// common case: "<curDay> days ago"
|
||||
GetAgeInDaysString(curDay, NS_LITERAL_STRING("finduri-AgeInDays-is").get(),
|
||||
dateNames[curDay]);
|
||||
}
|
||||
// special case: "Older than <numDays-2> days"
|
||||
GetAgeInDaysString(numDays-2,
|
||||
NS_LITERAL_STRING("finduri-AgeInDays-isgreater").get(),
|
||||
dateNames[numDays-1]);
|
||||
|
||||
PRTime normalizedNow = NormalizeTimeRelativeToday(PR_Now());
|
||||
|
||||
for (PRInt32 i = 0; i < aSource.Count(); i ++) {
|
||||
if (!aSource[i]->IsURI()) {
|
||||
// what do we do with non-URLs? I'll just dump them into the top level
|
||||
aDest->AppendObject(aSource[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// get the date from aSource[i]
|
||||
nsCAutoString curDateName;
|
||||
PRInt64 ageInDays = GetAgeInDays(normalizedNow, aSource[i]->mTime);
|
||||
if (ageInDays > (numDays - 1))
|
||||
ageInDays = numDays - 1;
|
||||
curDateName = dateNames[ageInDays];
|
||||
|
||||
if (!dates[ageInDays]) {
|
||||
// need to create an entry for this date
|
||||
dates[ageInDays] = new nsNavHistoryContainerResultNode(EmptyCString(),
|
||||
curDateName,
|
||||
EmptyCString(),
|
||||
nsNavHistoryResultNode::RESULT_TYPE_DAY,
|
||||
PR_TRUE,
|
||||
EmptyCString());
|
||||
|
||||
if (!dates[ageInDays])
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
if (!dates[ageInDays]->mChildren.AppendObject(aSource[i]))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
for (PRInt32 i = 0; i < numDays; i++) {
|
||||
if (dates[i]) {
|
||||
nsresult rv = aDest->AppendObject(dates[i]);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsNavHistory::GroupByHost
|
||||
//
|
||||
@ -3768,15 +3863,34 @@ nsNavHistory::TitleForDomain(const nsCString& domain, nsACString& aTitle)
|
||||
}
|
||||
|
||||
// use the localized one instead
|
||||
nsXPIDLString value;
|
||||
nsresult rv = mBundle->GetStringFromName(
|
||||
NS_LITERAL_STRING("localhost").get(), getter_Copies(value));
|
||||
if (NS_SUCCEEDED(rv))
|
||||
CopyUTF16toUTF8(value, aTitle);
|
||||
else
|
||||
aTitle.Truncate(0);
|
||||
GetStringFromName(NS_LITERAL_STRING("localhost").get(), aTitle);
|
||||
}
|
||||
|
||||
void
|
||||
nsNavHistory::GetAgeInDaysString(PRInt32 aInt, const PRUnichar *aName, nsACString& aResult)
|
||||
{
|
||||
nsAutoString intString;
|
||||
intString.AppendInt(aInt);
|
||||
const PRUnichar* strings[1] = { intString.get() };
|
||||
nsXPIDLString value;
|
||||
nsresult rv = mBundle->FormatStringFromName(aName, strings,
|
||||
1, getter_Copies(value));
|
||||
if (NS_SUCCEEDED(rv))
|
||||
CopyUTF16toUTF8(value, aResult);
|
||||
else
|
||||
aResult.Truncate(0);
|
||||
}
|
||||
|
||||
void
|
||||
nsNavHistory::GetStringFromName(const PRUnichar *aName, nsACString& aResult)
|
||||
{
|
||||
nsXPIDLString value;
|
||||
nsresult rv = mBundle->GetStringFromName(aName, getter_Copies(value));
|
||||
if (NS_SUCCEEDED(rv))
|
||||
CopyUTF16toUTF8(value, aResult);
|
||||
else
|
||||
aResult.Truncate(0);
|
||||
}
|
||||
|
||||
// nsNavHistory::SetPageTitleInternal
|
||||
//
|
||||
|
||||
@ -477,10 +477,19 @@ protected:
|
||||
nsNavHistoryQueryOptions* aOptions,
|
||||
nsCOMArray<nsNavHistoryResultNode>* aResults);
|
||||
|
||||
void GetAgeInDaysString(PRInt32 aInt, const PRUnichar *aName,
|
||||
nsACString& aResult);
|
||||
|
||||
void GetStringFromName(const PRUnichar *aName, nsACString& aResult);
|
||||
|
||||
void TitleForDomain(const nsCString& domain, nsACString& aTitle);
|
||||
|
||||
nsresult SetPageTitleInternal(nsIURI* aURI, PRBool aIsUserTitle,
|
||||
const nsAString& aTitle);
|
||||
|
||||
nsresult GroupByDay(const nsCOMArray<nsNavHistoryResultNode>& aSource,
|
||||
nsCOMArray<nsNavHistoryResultNode>* aDest);
|
||||
|
||||
nsresult GroupByHost(const nsCOMArray<nsNavHistoryResultNode>& aSource,
|
||||
nsCOMArray<nsNavHistoryResultNode>* aDest,
|
||||
PRBool aIsDomain);
|
||||
|
||||
@ -445,6 +445,9 @@ nsNavHistoryContainerResultNode::FillStats()
|
||||
container->FillStats();
|
||||
}
|
||||
mAccessCount += node->mAccessCount;
|
||||
// this is how container nodes get sorted by date
|
||||
// (of type nsINavHistoryResultNode::RESULT_TYPE_DAY, for example)
|
||||
// The container gets the most recent time of the child nodes.
|
||||
if (node->mTime > mTime)
|
||||
mTime = node->mTime;
|
||||
}
|
||||
@ -698,6 +701,18 @@ PRInt32 PR_CALLBACK nsNavHistoryContainerResultNode::SortComparison_TitleLess(
|
||||
return bType - aType;
|
||||
}
|
||||
|
||||
if (aType == nsINavHistoryResultNode::RESULT_TYPE_DAY) {
|
||||
// for the history sidebar, when we do "View | By Date" or
|
||||
// "View | By Date and Site" we sort by SORT_BY_TITLE_ASCENDING.
|
||||
//
|
||||
// so to make the day container show up in the desired order
|
||||
// we need to compare by time, instead of by title.
|
||||
//
|
||||
// hard coding this isn't ideal, but we can't currently have
|
||||
// one sort per grouping. see bug #359332 on that issue.
|
||||
return -ComparePRTime(a->mTime, b->mTime);
|
||||
}
|
||||
|
||||
nsICollation* collation = NS_STATIC_CAST(nsICollation*, closure);
|
||||
PRInt32 value = -1; // default to returning "true" on failure
|
||||
collation->CompareString(
|
||||
@ -1983,11 +1998,11 @@ nsNavHistoryQueryResultNode::GetSortType()
|
||||
} else if (mResult) {
|
||||
return mResult->mSortingMode;
|
||||
}
|
||||
|
||||
NS_NOTREACHED("We should always have a result");
|
||||
return nsINavHistoryQueryOptions::SORT_BY_NONE;
|
||||
}
|
||||
|
||||
|
||||
// nsNavHistoryResultNode::OnBeginUpdateBatch
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -3864,10 +3879,14 @@ nsNavHistoryResultTreeViewer::ComputeShowSessions()
|
||||
mResult->mSortingMode != nsINavHistoryQueryOptions::SORT_BY_DATE_DESCENDING)
|
||||
return; // not date sorting
|
||||
|
||||
// showing sessions only makes sense if we are grouping by date
|
||||
// any other grouping (or recursive grouping) doesn't make sense
|
||||
PRUint32 groupCount;
|
||||
options->GroupingMode(&groupCount);
|
||||
if (groupCount > 0)
|
||||
return;
|
||||
const PRUint32* groupings = options->GroupingMode(&groupCount);
|
||||
for (PRUint32 i = 0; i < groupCount; i ++) {
|
||||
if (groupings[i] != nsINavHistoryQueryOptions::GROUP_BY_DAY)
|
||||
return; // non-time-based grouping
|
||||
}
|
||||
|
||||
mShowSessions = PR_TRUE;
|
||||
}
|
||||
|
||||
@ -275,7 +275,8 @@ public:
|
||||
return (type == nsINavHistoryResultNode::RESULT_TYPE_HOST ||
|
||||
type == nsINavHistoryResultNode::RESULT_TYPE_REMOTE_CONTAINER ||
|
||||
type == nsINavHistoryResultNode::RESULT_TYPE_QUERY ||
|
||||
type == nsINavHistoryResultNode::RESULT_TYPE_FOLDER);
|
||||
type == nsINavHistoryResultNode::RESULT_TYPE_FOLDER ||
|
||||
type == nsINavHistoryResultNode::RESULT_TYPE_DAY);
|
||||
}
|
||||
PRBool IsContainer() {
|
||||
PRUint32 type;
|
||||
@ -287,7 +288,8 @@ public:
|
||||
// itself, and is used when recursively updating a query. This currently
|
||||
// includes only host containers, but may be extended to support things
|
||||
// like days or other criteria. It doesn't include other queries and folders.
|
||||
return (type == nsINavHistoryResultNode::RESULT_TYPE_HOST);
|
||||
return (type == nsINavHistoryResultNode::RESULT_TYPE_HOST ||
|
||||
type == nsINavHistoryResultNode::RESULT_TYPE_DAY);
|
||||
}
|
||||
PRBool IsQuerySubcontainer() {
|
||||
PRUint32 type;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user