Add support for bookmark separators (bug 320261). r=brettw, ben.

This commit is contained in:
bryner%brianryner.com 2006-02-08 01:10:57 +00:00
Родитель 0912af765f
Коммит 14e56de3fd
16 изменённых файлов: 537 добавлений и 45 удалений

Просмотреть файл

@ -61,7 +61,8 @@
<command id="placesCmd_new:folder"
label="&cmd.new_folder.label;" accesskey="&cmd.new_folder.accesskey;"
oncommand="PlacesController.newFolder()"/>
<command id="placesCmd_new:separator" label="&cmd.new_separator.label;" accesskey="&cmd.new_separator.accesskey;"/>
<command id="placesCmd_new:separator" label="&cmd.new_separator.label;" accesskey="&cmd.new_separator.accesskey;"
oncommand="PlacesController.newSeparator()"/>
</commandset>
<commandset type="container|feed" readonly="true">
<command id="placesCmd_reload" label="&cmd.reload.label;" accesskey="&cmd.reload.accesskey;"/>

Просмотреть файл

@ -12,6 +12,8 @@
selection="link|links|folder"/>
<menuitem id="placesContext_new:folder" command="placesCmd_new:folder"
selection="mutable"/>
<menuitem id="placesContext_new:separator" command="placesCmd_new:separator"
selection="mutable"/>
<menuseparator id="placesContext_newSeparator"
selection="mutable"/>
<menuitem id="placesContext_edit:cut" command="placesCmd_edit:cut"

Просмотреть файл

@ -86,7 +86,9 @@ const SELECTION_IS_MOVABLE = 0x40;
// Place entries that are containers, e.g. bookmark folders or queries.
const TYPE_X_MOZ_PLACE_CONTAINER = "text/x-moz-place-container";
// Place entries that are not containers
// Place entries that are bookmark separators.
const TYPE_X_MOZ_PLACE_SEPARATOR = "text/x-moz-place-separator";
// Place entries that are not containers or separators
const TYPE_X_MOZ_PLACE = "text/x-moz-place";
// Place entries in shortcut url format (url\ntitle)
const TYPE_X_MOZ_URL = "text/x-moz-url";
@ -145,8 +147,9 @@ function ViewConfig(dropTypes, dropOnTypes, excludeItems, expandQueries, firstDr
this.firstDropIndex = firstDropIndex;
this.filterTransactions = filterTransactions;
}
ViewConfig.GENERIC_DROP_TYPES = [TYPE_X_MOZ_PLACE_CONTAINER, TYPE_X_MOZ_PLACE,
TYPE_X_MOZ_URL];
ViewConfig.GENERIC_DROP_TYPES = [TYPE_X_MOZ_PLACE_CONTAINER,
TYPE_X_MOZ_PLACE_SEPARATOR, TYPE_X_MOZ_PLACE,
TYPE_X_MOZ_URL]
/**
* Manages grouping options for a particular view type.
@ -283,7 +286,7 @@ var PlacesController = {
},
/**
* Generates a HistoryResult for the contents of a folder.
* Generates a HistoryResultNode for the contents of a folder.
* @param folderId
* The folder to open
* @param excludeItems
@ -293,7 +296,7 @@ var PlacesController = {
* True to make query items expand as new containers. For managing,
* you want this to be false, for menus and such, you want this to
* be true.
* @returns A HistoryResult containing the contents of the folder.
* @returns A HistoryResultNode containing the contents of the folder.
*/
getFolderContents: function PC_getFolderContents(folderId, excludeItems, expandQueries) {
var query = this._hist.getNewQuery();
@ -463,6 +466,16 @@ var PlacesController = {
return (node.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER);
},
/**
* Determines whether or not a ResultNode is a Bookmark separator.
* @param node
* A NavHistoryResultNode
* @returns true if the node is a Bookmark separator, false otherwise
*/
nodeIsSeparator: function PC_nodeIsSeparator(node) {
return (node.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR);
},
/**
* Determines whether or not a ResultNode is a URL item or not
* @param node
@ -570,8 +583,7 @@ var PlacesController = {
// New Folder
this._setEnabled("placesCmd_new:folder", viewIsFolder);
// New Separator
// ...
this._setEnabled("placesCmd_new:separator", false);
this._setEnabled("placesCmd_new:separator", viewIsFolder);
// Feed Reload
this._setEnabled("placesCmd_reload", false);
},
@ -843,7 +855,17 @@ var PlacesController = {
view.restoreSelection();
}
},
/**
* Create a new Bookmark separator somewhere.
*/
newSeparator: function PC_newSeparator() {
var ip = this._activeView.insertionPoint;
var txn = new PlacesInsertSeparatorTransaction(ip.folderId, ip.index);
this.tm.doTransaction(txn);
this._activeView.focus();
},
/**
* Creates a set of transactions for the removal of a range of items. A range is
* an array of adjacent nodes in a view.
@ -864,6 +886,11 @@ var PlacesController = {
transactions.push(new PlacesRemoveFolderTransaction(
asFolder(node).folderId, asFolder(node.parent).folderId, index));
}
else if (this.nodeIsSeparator(node)) {
// A Bookmark separator.
transactions.push(new PlacesRemoveSeparatorTransaction(
asFolder(node.parent).folderId, index));
}
else if (this.nodeIsFolder(node.parent)) {
// A Bookmark in a Bookmark Folder.
transactions.push(new PlacesRemoveItemTransaction(
@ -956,6 +983,7 @@ var PlacesController = {
switch (type) {
case TYPE_X_MOZ_PLACE_CONTAINER:
case TYPE_X_MOZ_PLACE:
case TYPE_X_MOZ_PLACE_SEPARATOR:
var wrapped = "";
if (this.nodeIsFolder(node))
wrapped += asFolder(node).folderId + "\n";
@ -1000,6 +1028,7 @@ var PlacesController = {
switch (type) {
case TYPE_X_MOZ_PLACE_CONTAINER:
case TYPE_X_MOZ_PLACE:
case TYPE_X_MOZ_PLACE_SEPARATOR:
nodes.push({ folderId: parseInt(parts[i++]),
uri: parts[i] ? this._uri(parts[i]) : null,
parent: parseInt(parts[++i]),
@ -1111,6 +1140,19 @@ var PlacesController = {
return new PlacesMoveItemTransaction(data.uri, data.parent,
data.index, container,
index);
case TYPE_X_MOZ_PLACE_SEPARATOR:
if (copy) {
// There is no data in a separator, so copying it just amounts to
// inserting a new separator.
return new PlacesInsertSeparatorTransaction(container, index);
}
// Similarly, moving a separator is just removing the old one and
// then creating a new one.
var removeTxn =
new PlacesRemoveSeparatorTransaction(data.parent, data.index);
var createTxn =
new PlacesInsertSeparatorTransaction(container, index);
return new PlacesAggregateTransaction("SeparatorMove", [removeTxn, createTxn]);
case TYPE_X_MOZ_URL:
// Creating and Setting the title is a two step process, so create
// a transaction for each then aggregate them.
@ -1170,6 +1212,9 @@ var PlacesController = {
addData(TYPE_X_MOZ_PLACE_CONTAINER);
}
else if (this.nodeIsSeparator(node)) {
addData(TYPE_X_MOZ_PLACE_SEPARATOR);
}
else {
// This order is _important_! It controls how this and other
// applications select data to be inserted based on type.
@ -1193,7 +1238,7 @@ var PlacesController = {
Cc["@mozilla.org/widget/transferable;1"].
createInstance(Ci.nsITransferable);
var foundFolder = false, foundLink = false;
var pcString = placeString = mozURLString = htmlString = unicodeString = "";
var pcString = psString = placeString = mozURLString = htmlString = unicodeString = "";
for (var i = 0; i < nodes.length; ++i) {
var node = nodes[i];
var self = this;
@ -1203,6 +1248,9 @@ var PlacesController = {
}
if (this.nodeIsFolder(node) || this.nodeIsQuery(node))
pcString += generateChunk(TYPE_X_MOZ_PLACE_CONTAINER);
else if (this.nodeIsSeparator(node)) {
psString += generateChunk(TYPE_X_MOZ_PLACE_SEPARATOR);
}
else {
placeString += generateChunk(TYPE_X_MOZ_PLACE);
mozURLString += generateChunk(TYPE_X_MOZ_URL);
@ -1220,6 +1268,8 @@ var PlacesController = {
// select data to be inserted based on type.
if (pcString)
addData(TYPE_X_MOZ_PLACE_CONTAINER, pcString);
if (psString)
addData(TYPE_X_MOZ_PLACE_SEPARATOR, psString);
if (placeString)
addData(TYPE_X_MOZ_PLACE, placeString);
if (unicodeString)
@ -1229,7 +1279,7 @@ var PlacesController = {
if (mozURLString)
addData(TYPE_X_MOZ_URL, mozURLString);
if (pcString || placeString || unicodeString || htmlString ||
if (pcString || psString || placeString || unicodeString || htmlString ||
mozURLString) {
var clipboard =
Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard);
@ -1253,6 +1303,7 @@ var PlacesController = {
Cc["@mozilla.org/widget/transferable;1"].
createInstance(Ci.nsITransferable);
xferable.addDataFlavor(TYPE_X_MOZ_PLACE_CONTAINER);
xferable.addDataFlavor(TYPE_X_MOZ_PLACE_SEPARATOR);
xferable.addDataFlavor(TYPE_X_MOZ_PLACE);
xferable.addDataFlavor(TYPE_X_MOZ_URL);
xferable.addDataFlavor(TYPE_UNICODE);
@ -1496,6 +1547,28 @@ PlacesCreateItemTransaction.prototype = {
},
};
/**
* Create a new Separator
*/
function PlacesInsertSeparatorTransaction(container, index) {
this._container = container;
this._index = index;
this._id = null;
}
PlacesInsertSeparatorTransaction.prototype = {
__proto__: PlacesBaseTransaction.prototype,
doTransaction: function PIST_doTransaction() {
LOG("Create separator in: " + this._container + "," + this._index);
this._id = this._bms.insertSeparator(this._container, this._index);
},
undoTransaction: function PIST_undoTransaction() {
LOG("UNCreate separator from: " + this._container + "," + this._index);
this._bms.removeChildAt(this._container, this._index);
},
};
/**
* Move a Folder
*/
@ -1686,6 +1759,27 @@ PlacesRemoveItemTransaction.prototype = {
},
};
/**
* Remove a separator
*/
function PlacesRemoveSeparatorTransaction(oldContainer, oldIndex) {
this._oldContainer = oldContainer;
this._oldIndex = oldIndex;
}
PlacesRemoveSeparatorTransaction.prototype = {
__proto__: PlacesBaseTransaction.prototype,
doTransaction: function PRST_doTransaction() {
LOG("Remove Separator from: " + this._oldContainer + "," + this._oldIndex);
this._bms.removeChildAt(this._oldContainer, this._oldIndex);
},
undoTransaction: function PRST_undoTransaction() {
LOG("UNRemove Separator from: " + this._oldContainer + "," + this._oldIndex);
this._bms.insertSeparator(this._oldContainer, this._oldIndex);
},
};
/**
* Edit a Folder
*/

Просмотреть файл

@ -206,11 +206,11 @@
<field name="filterTransactions">false</field>
<field name="supportedDropTypes">
[TYPE_X_MOZ_PLACE_CONTAINER, TYPE_X_MOZ_PLACE, TYPE_X_MOZ_URL]
[TYPE_X_MOZ_PLACE_CONTAINER, TYPE_X_MOZ_PLACE_SEPARATOR, TYPE_X_MOZ_PLACE, TYPE_X_MOZ_URL]
</field>
<field name="supportedDropOnTypes">
[TYPE_X_MOZ_PLACE_CONTAINER, TYPE_X_MOZ_PLACE, TYPE_X_MOZ_URL]
[TYPE_X_MOZ_PLACE_CONTAINER, TYPE_X_MOZ_PLACE_SEPARATOR, TYPE_X_MOZ_PLACE, TYPE_X_MOZ_URL]
</field>
<method name="selectAll">

Просмотреть файл

@ -247,11 +247,11 @@
<field name="filterTransactions">false</field>
<field name="supportedDropTypes">
[TYPE_X_MOZ_PLACE_CONTAINER, TYPE_X_MOZ_PLACE, TYPE_X_MOZ_URL]
[TYPE_X_MOZ_PLACE_CONTAINER, TYPE_X_MOZ_PLACE_SEPARATOR, TYPE_X_MOZ_PLACE, TYPE_X_MOZ_URL]
</field>
<field name="supportedDropOnTypes">
[TYPE_X_MOZ_PLACE_CONTAINER, TYPE_X_MOZ_PLACE, TYPE_X_MOZ_URL]
[TYPE_X_MOZ_PLACE_CONTAINER, TYPE_X_MOZ_PLACE_SEPARATOR, TYPE_X_MOZ_PLACE, TYPE_X_MOZ_URL]
</field>
<method name="selectAll">
@ -316,6 +316,14 @@
}
setTimeout(hitch(this._self, this._self._rebuild), 1);
},
onSeparatorAdded: function TB_O_onSeparatorAdded(parent, index) {
if (!this._numBatches)
this._self.init();
},
onSeparatorRemoved: function TB_O_onSeparatorRemoved(parent, index) {
if (!this._numBatches)
this._self.init();
}
})]]></field>
<field name="_DNDObserver"><![CDATA[({
// XXXben ew.

Просмотреть файл

@ -433,16 +433,16 @@
<parameter name="insertionPoint"/>
<parameter name="excludeItems"/>
<body><![CDATA[
var result =
var folder =
PlacesController.getFolderContents(insertionPoint.folderId,
excludeItems, false);
var index = insertionPoint.index;
if (insertionPoint.index == 0)
index = 0;
else if (insertionPoint.index == -1 || insertionPoint.index >= result.root.childCount)
index = result.root.childCount - 1;
ASSERT(index < result.childCount, "index out of range: " + index + " > " + result);
return index > -1 ? result.root.getChild(index) : null;
else if (insertionPoint.index == -1 || insertionPoint.index >= folder.childCount)
index = folder.childCount - 1;
ASSERT(index < folder.childCount, "index out of range: " + index + " > " + folder);
return index > -1 ? folder.getChild(index) : null;
]]></body>
</method>

Просмотреть файл

@ -162,6 +162,20 @@ interface nsINavBookmarkObserver : nsISupports
* @param property The property that was changed.
*/
void onFolderChanged(in PRInt64 folder, in ACString property);
/**
* Notify this observer that a separator has been added.
* @param parent The id of the separator's parent.
* @param index The separator's index inside its parent.
*/
void onSeparatorAdded(in PRInt64 parent, in PRInt32 index);
/**
* Notify this observer that a separator has been removed.
* @param parent The id of the separator's parent.
* @param index The separator's old index in its parent.
*/
void onSeparatorRemoved(in PRInt64 parent, in PRInt32 index);
};
/**
@ -263,6 +277,21 @@ interface nsINavBookmarksService : nsISupports
*/
PRInt64 getChildFolder(in PRInt64 folder, in AString subFolder);
/**
* Inserts a bookmark separator into the given folder at the given index.
* The separator can be removed using removeChildAt().
* @param folder Parent folder of the separator
* @param index The separator's index under folder, or -1 to append
*/
void insertSeparator(in PRInt64 folder, in PRInt32 index);
/**
* Removes any type of child (item, folder, or separator) at the given index.
* @param folder The folder to remove a child from
* @param index The index of the child to remove
*/
void removeChildAt(in PRInt64 folder, in PRInt32 index);
/**
* Set the history/bookmark title for a URI. The new title will be used
* anywhere the URI is shown in bookmarks or history.

Просмотреть файл

@ -81,6 +81,7 @@ interface nsINavHistoryResultNode : nsISupports
const PRUint32 RESULT_TYPE_REMOTE_CONTAINER = 4; // nsINavHistoryContainerResultNode
const PRUint32 RESULT_TYPE_QUERY = 5; // nsINavHistoryQueryResultNode
const PRUint32 RESULT_TYPE_FOLDER = 6; // nsINavHistoryFolderResultNode
const PRUint32 RESULT_TYPE_SEPARATOR = 7; // nsINavHistoryResultNode
readonly attribute PRUint32 type;
/**
@ -814,10 +815,10 @@ interface nsINavHistoryQueryOptions : nsISupports
attribute PRUint32 resultType;
/**
* This option excludes all URIs from a bookmarks query. This would be used
* if you just wanted a list of bookmark folders and queries (such as the left
* pane of the places page). Ignored for queries over history.
* Defaults to false.
* This option excludes all URIs and separators from a bookmarks query.
* This would be used if you just wanted a list of bookmark folders and
* queries (such as the left pane of the places page).
* Ignored for queries over history. Defaults to false.
*/
attribute boolean excludeItems;

Просмотреть файл

@ -39,7 +39,7 @@
-moz-appearance: listbox;
}
treechildren::-moz-tree-image(title) {
treechildren::-moz-tree-image(title, container) {
padding-right: 2px;
margin: 0px 2px;
list-style-image: url("chrome://global/skin/icons/folder-item.png") !important;

Просмотреть файл

@ -272,6 +272,7 @@ protected:
void HandleHeadEnd();
void HandleLinkBegin(const nsIParserNode& node);
void HandleLinkEnd();
void HandleSeparator();
// This is a list of frames. We really want a recursive parser, but the HTML
// parser gives us tags as a stream. This implements all the state on a stack
@ -410,9 +411,16 @@ BookmarkContentSink::CloseContainer(const nsHTMLTag aTag)
NS_IMETHODIMP
BookmarkContentSink::AddLeaf(const nsIParserNode& aNode)
{
// save any text we find
if (aNode.GetNodeType() == eHTMLTag_text) {
CurFrame().mPreviousText += aNode.GetText();
switch (aNode.GetNodeType()) {
case eHTMLTag_text:
// save any text we find
if (aNode.GetNodeType() == eHTMLTag_text) {
CurFrame().mPreviousText += aNode.GetText();
}
break;
case eHTMLTag_hr:
HandleSeparator();
break;
}
return NS_OK;
@ -640,6 +648,24 @@ BookmarkContentSink::HandleLinkEnd()
}
// BookmarkContentSink::HandleSeparator
//
// Inserts a separator into the current container
void
BookmarkContentSink::HandleSeparator()
{
BookmarkImportFrame& frame = CurFrame();
// bookmarks.html contains a separator between the toolbar menu and the
// rest of the items. Since we pull the toolbar menu out into the top level,
// we want to skip over this separator since it looks out of place.
if (frame.mLastContainerType != BookmarkImportFrame::Container_Toolbar) {
// create the separator
mBookmarksService->InsertSeparator(frame.mContainerID, -1);
}
}
// BookmarkContentSink::NewFrame
//
// This is called when there is a new folder found. The folder takes the

Просмотреть файл

@ -189,12 +189,26 @@ nsNavBookmarks::Init()
"JOIN moz_bookmarks_folders c ON c.id = a.folder_child "
"WHERE a.parent = ?1 AND a.position >= ?2 AND a.position <= ?3");
// Construct a result where the first columns are padded out to the width
// of mDBGetVisitPageInfo, containing additional columns for position,
// item_child, and folder_child from moz_bookmarks. This selects only
// _separator_ children which are in moz_bookmarks. Results are
// kGetInfoIndex_* kGetChildrenIndex_*. item_child and folder_child will
// be NULL for separators.
NS_NAMED_LITERAL_CSTRING(selectSeparatorChildren,
"SELECT null, null, null, null, null, null, null, null, null, a.position, null, null, null "
"FROM moz_bookmarks a "
"WHERE a.parent = ?1 AND a.position >= ?2 AND a.position <= ?3 AND "
"a.item_child ISNULL and a.folder_child ISNULL");
NS_NAMED_LITERAL_CSTRING(orderByPosition, " ORDER BY a.position");
// mDBGetChildren: select all children of a given folder, sorted by position
rv = dbConn->CreateStatement(selectItemChildren +
NS_LITERAL_CSTRING(" UNION ALL ") +
selectFolderChildren + orderByPosition,
selectFolderChildren +
NS_LITERAL_CSTRING(" UNION ALL ") +
selectSeparatorChildren + orderByPosition,
getter_AddRefs(mDBGetChildren));
NS_ENSURE_SUCCESS(rv, rv);
@ -212,6 +226,10 @@ nsNavBookmarks::Init()
getter_AddRefs(mDBIndexOfFolder));
NS_ENSURE_SUCCESS(rv, rv);
rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("SELECT item_child, folder_child FROM moz_bookmarks WHERE parent = ?1 AND position = ?2"),
getter_AddRefs(mDBGetChildAt));
NS_ENSURE_SUCCESS(rv, rv);
rv = InitRoots();
NS_ENSURE_SUCCESS(rv, rv);
@ -446,7 +464,7 @@ nsNavBookmarks::AdjustIndices(PRInt64 aFolder,
if (item->itemURI) {
nsIURI *uri = item->itemURI;
obs->OnItemMoved(uri, aFolder, oldPosition, newPosition);
} else {
} else if (item->folderChild) {
obs->OnFolderMoved(item->folderChild, aFolder, oldPosition,
aFolder, newPosition);
}
@ -709,6 +727,135 @@ nsNavBookmarks::CreateContainer(PRInt64 aParent, const nsAString &aName,
}
NS_IMETHODIMP
nsNavBookmarks::InsertSeparator(PRInt64 aParent, PRInt32 aIndex)
{
mozIStorageConnection *dbConn = DBConn();
mozStorageTransaction transaction(dbConn, PR_FALSE);
PRInt32 index = (aIndex == -1) ? FolderCount(aParent) : aIndex;
nsresult rv = AdjustIndices(aParent, index, PR_INT32_MAX, 1);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<mozIStorageStatement> statement;
rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("INSERT INTO moz_bookmarks "
"(parent, position) VALUES (?1,?2)"),
getter_AddRefs(statement));
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->BindInt64Parameter(0, aParent);
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->BindInt32Parameter(1, index);
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
rv = transaction.Commit();
NS_ENSURE_SUCCESS(rv, rv);
ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
OnSeparatorAdded(aParent, index))
return NS_OK;
}
NS_IMETHODIMP
nsNavBookmarks::RemoveChildAt(PRInt64 aParent, PRInt32 aIndex)
{
mozIStorageConnection *dbConn = DBConn();
mozStorageTransaction transaction(dbConn, PR_FALSE);
nsresult rv;
PRInt64 item, folder;
{
mozStorageStatementScoper scope(mDBGetChildAt);
rv = mDBGetChildAt->BindInt64Parameter(0, aParent);
NS_ENSURE_SUCCESS(rv, rv);
rv = mDBGetChildAt->BindInt32Parameter(1, aIndex);
NS_ENSURE_SUCCESS(rv, rv);
PRBool hasMore;
rv = mDBGetChildAt->ExecuteStep(&hasMore);
NS_ENSURE_SUCCESS(rv, rv);
if (!hasMore) {
// Child doesn't exist
return NS_ERROR_INVALID_ARG;
}
if (mDBGetChildAt->IsNull(0)) {
item = 0;
folder = mDBGetChildAt->AsInt64(1);
} else {
folder = 0;
item = mDBGetChildAt->AsInt64(0);
}
}
if (item != 0) {
// We're removing an item, go find its URI.
mozIStorageStatement *pageInfo = History()->DBGetIdPageInfo();
nsCOMPtr<nsIURI> uri;
{
mozStorageStatementScoper scope(pageInfo);
rv = pageInfo->BindInt64Parameter(0, item);
NS_ENSURE_SUCCESS(rv, rv);
PRBool hasMore;
rv = pageInfo->ExecuteStep(&hasMore);
NS_ENSURE_SUCCESS(rv, rv);
if (!hasMore) {
return NS_ERROR_INVALID_ARG;
}
nsCAutoString spec;
rv = pageInfo->GetUTF8String(1, spec);
NS_ENSURE_SUCCESS(rv, rv);
rv = NS_NewURI(getter_AddRefs(uri), spec);
NS_ENSURE_SUCCESS(rv, rv);
}
// Commit this transaction so that we don't notify observers mid-tranaction
rv = transaction.Commit();
NS_ENSURE_SUCCESS(rv, rv);
return RemoveItem(aParent, uri);
}
if (folder != 0) {
// Commit this transaction so that we don't notify observers mid-tranaction
rv = transaction.Commit();
NS_ENSURE_SUCCESS(rv, rv);
return RemoveFolder(folder);
}
// No item or folder, so this is a separator.
nsCOMPtr<mozIStorageStatement> statement;
rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("DELETE FROM moz_bookmarks WHERE parent = ?1 AND position = ?2"),
getter_AddRefs(statement));
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->BindInt64Parameter(0, aParent);
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->BindInt32Parameter(1, aIndex);
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
rv = AdjustIndices(aParent, aIndex + 1, PR_INT32_MAX, -1);
NS_ENSURE_SUCCESS(rv, rv);
rv = transaction.Commit();
NS_ENSURE_SUCCESS(rv, rv);
ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
OnSeparatorRemoved(aParent, aIndex))
return NS_OK;
}
NS_IMETHODIMP
nsNavBookmarks::RemoveFolder(PRInt64 aFolder)
{
@ -785,6 +932,7 @@ nsNavBookmarks::RemoveFolderChildren(PRInt64 aFolder)
{
mozStorageTransaction transaction(DBConn(), PR_FALSE);
nsTArray<PRInt32> separatorChildren; // separator indices
nsTArray<PRInt64> folderChildren;
nsCOMArray<nsIURI> itemChildren;
nsresult rv;
@ -805,6 +953,9 @@ nsNavBookmarks::RemoveFolderChildren(PRInt64 aFolder)
// folder
folderChildren.AppendElement(
mDBGetChildren->AsInt64(kGetChildrenIndex_FolderChild));
} else if (mDBGetChildren->IsNull(kGetChildrenIndex_ItemChild)) {
// separator
separatorChildren.AppendElement(mDBGetChildren->AsInt32(kGetChildrenIndex_Position));
} else {
// item (URI)
nsCOMPtr<nsIURI> uri;
@ -818,8 +969,16 @@ nsNavBookmarks::RemoveFolderChildren(PRInt64 aFolder)
}
}
// remove folders
// Remove separators. The list of separators will already be sorted since
// we order by position, so by enumerating it backwards, we avoid having to
// deal with shifting indices.
PRUint32 i;
for (i = separatorChildren.Length() - 1; i != PRUint32(-1); --i) {
rv = RemoveChildAt(aFolder, separatorChildren[i]);
NS_ENSURE_SUCCESS(rv, rv);
}
// remove folders
for (i = 0; i < folderChildren.Length(); ++i) {
rv = RemoveFolder(folderChildren[i]);
NS_ENSURE_SUCCESS(rv, rv);
@ -1201,6 +1360,13 @@ nsNavBookmarks::QueryFolderChildren(PRInt64 aFolderId,
rv = ResultNodeForFolder(folder, aOptions, getter_AddRefs(node));
if (NS_FAILED(rv))
continue;
} else if (mDBGetChildren->IsNull(kGetChildrenIndex_ItemChild)) {
// separator
if (aOptions->ExcludeItems()) {
continue;
}
node = new nsNavHistorySeparatorResultNode();
NS_ENSURE_TRUE(node, NS_ERROR_OUT_OF_MEMORY);
} else {
rv = History()->RowToResult(mDBGetChildren, options,
getter_AddRefs(node));

Просмотреть файл

@ -130,6 +130,7 @@ private:
nsCOMPtr<mozIStorageStatement> mDBIndexOfItem;
nsCOMPtr<mozIStorageStatement> mDBIndexOfFolder;
nsCOMPtr<mozIStorageStatement> mDBGetChildAt;
nsCOMPtr<nsIStringBundle> mBundle;

Просмотреть файл

@ -183,6 +183,7 @@ nsIAtom* nsNavHistory::sMenuRootAtom = nsnull;
nsIAtom* nsNavHistory::sToolbarRootAtom = nsnull;
nsIAtom* nsNavHistory::sSessionStartAtom = nsnull;
nsIAtom* nsNavHistory::sSessionContinueAtom = nsnull;
nsIAtom* nsNavHistory::sContainerAtom = nsnull;
nsNavHistory* nsNavHistory::gHistoryService;
@ -199,6 +200,7 @@ nsNavHistory::nsNavHistory() : mNowValid(PR_FALSE),
sToolbarRootAtom = NS_NewAtom("toolbar-root");
sSessionStartAtom = NS_NewAtom("session-start");
sSessionContinueAtom = NS_NewAtom("session-continue");
sContainerAtom = NS_NewAtom("container");
}
@ -219,6 +221,7 @@ nsNavHistory::~nsNavHistory()
NS_IF_RELEASE(sToolbarRootAtom);
NS_IF_RELEASE(sSessionStartAtom);
NS_IF_RELEASE(sSessionContinueAtom);
NS_IF_RELEASE(sContainerAtom);
}
@ -435,6 +438,13 @@ nsNavHistory::InitDB()
getter_AddRefs(mDBGetURLPageInfoFull));
NS_ENSURE_SUCCESS(rv, rv);
// mDBGetIdPageInfo
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
"SELECT h.id, h.url, h.title, h.user_title, h.rev_host, h.visit_count "
"FROM moz_history h WHERE h.id = ?1"),
getter_AddRefs(mDBGetIdPageInfo));
NS_ENSURE_SUCCESS(rv, rv);
// mDBGetIdPageInfoFull
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
"SELECT h.id, h.url, h.title, h.user_title, h.rev_host, h.visit_count, "

Просмотреть файл

@ -180,6 +180,9 @@ public:
mozIStorageStatement* DBGetURLPageInfoFull()
{ return mDBGetURLPageInfoFull; }
// select a history row by id
mozIStorageStatement* DBGetIdPageInfo() { return mDBGetIdPageInfo; }
// select a history row by id, with visit date info (extra work)
mozIStorageStatement* DBGetIdPageInfoFull()
{ return mDBGetIdPageInfoFull; }
@ -196,6 +199,7 @@ public:
static nsIAtom* sToolbarRootAtom;
static nsIAtom* sSessionStartAtom;
static nsIAtom* sSessionContinueAtom;
static nsIAtom* sContainerAtom;
// this actually executes a query and gives you results, it is used by
// nsNavHistoryQueryResultNode
@ -282,6 +286,7 @@ protected:
//nsCOMPtr<mozIStorageStatement> mDBGetVisitPageInfo; // kGetInfoIndex_* results
nsCOMPtr<mozIStorageStatement> mDBGetURLPageInfo; // kGetInfoIndex_* results
nsCOMPtr<mozIStorageStatement> mDBGetURLPageInfoFull; // kGetInfoIndex_* results
nsCOMPtr<mozIStorageStatement> mDBGetIdPageInfo; // kGetInfoIndex_* results
nsCOMPtr<mozIStorageStatement> mDBGetIdPageInfoFull; // kGetInfoIndex_* results
nsCOMPtr<mozIStorageStatement> mDBFullAutoComplete; // kAutoCompleteIndex_* results, 1 arg (max # results)
static const PRInt32 kAutoCompleteIndex_URL;

Просмотреть файл

@ -553,9 +553,8 @@ nsNavHistoryContainerResultNode::GetSortingComparator(PRUint32 aSortType)
// nsNavHistoryContainerResultNode::RecursiveSort
//
// This is used by Result::SortAll and it is static so it can also be used
// by nsNavHistory::GetQueryResults to sort node arrays without having a
// root container.
// This is used by Result::SortAll and QueryResultNode::FillChildren to sort
// the child list.
//
// This does NOT update any visibility or tree information. The caller will
// have to completely rebuild the visible list after this.
@ -2307,7 +2306,17 @@ nsNavHistoryQueryResultNode::OnFolderChanged(PRInt64 aFolder,
return Refresh();
return NS_OK;
}
NS_IMETHODIMP
nsNavHistoryQueryResultNode::OnSeparatorAdded(PRInt64 aParent, PRInt32 aIndex)
{
return NS_OK;
}
NS_IMETHODIMP
nsNavHistoryQueryResultNode::OnSeparatorRemoved(PRInt64 aParent,
PRInt32 aIndex)
{
return NS_OK;
}
// nsNavHistoryFolderResultNode ************************************************
//
@ -2959,6 +2968,87 @@ nsNavHistoryFolderResultNode::OnFolderChanged(PRInt64 aFolder,
return NS_OK;
}
// nsNavHistoryFolderResultNode::OnSeparatorAdded (nsINavBookmarkObserver)
NS_IMETHODIMP
nsNavHistoryFolderResultNode::OnSeparatorAdded(PRInt64 aParent, PRInt32 aIndex)
{
NS_ASSERTION(aParent == mFolderId, "Got wrong bookmark update");
if (! StartIncrementalUpdate())
return NS_OK;
// We remove all separators if the folder view is sorted, so only
// bother updating if there is no sort in effect.
if (GetSortType() != nsINavHistoryQueryOptions::SORT_BY_NONE) {
return NS_OK;
}
nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService();
NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY);
nsNavHistoryResultNode* node = new nsNavHistorySeparatorResultNode();
NS_ENSURE_TRUE(node, NS_ERROR_OUT_OF_MEMORY);
return InsertChildAt(node, aIndex);
}
// nsNavHistoryFolderResultNode::OnSeparatorRemoved (nsINavBookmarkObserver)
NS_IMETHODIMP
nsNavHistoryFolderResultNode::OnSeparatorRemoved(PRInt64 aParent,
PRInt32 aIndex)
{
NS_ASSERTION(aParent == mFolderId, "Got wrong bookmark update");
if (! StartIncrementalUpdate())
return NS_OK;
// We remove all separators if the folder view is sorted, so only
// bother updating if there is no sort in effect.
if (GetSortType() != nsINavHistoryQueryOptions::SORT_BY_NONE) {
return NS_OK;
}
if (aIndex >= mChildren.Count()) {
NS_NOTREACHED("Removing separator at invalid index");
return NS_OK;
}
if (!mChildren[aIndex]->IsSeparator()) {
NS_NOTREACHED("OnSeparatorRemoved called for a non-separator node");
return NS_OK;
}
return RemoveChildAt(aIndex);
}
// nsNavHistoryFolderResultNode::RecursiveSort
void
nsNavHistoryFolderResultNode::RecursiveSort(
nsICollation* aCollation, SortComparator aComparator)
{
if (GetSortType() != nsINavHistoryQueryOptions::SORT_BY_NONE) {
// We weren't sorted before, but now we are.
// Remove any separators so that they don't appear in the sorted list.
for (PRUint32 i = mChildren.Count() - 1; i != PRUint32(-1); --i) {
if (mChildren[i]->IsSeparator()) {
mChildren.RemoveObjectAt(i);
}
}
}
nsNavHistoryContainerResultNode::RecursiveSort(aCollation, aComparator);
}
// nsNavHistorySeparatorResultNode
//
// Separator nodes do not hold any data
nsNavHistorySeparatorResultNode::nsNavHistorySeparatorResultNode()
: nsNavHistoryResultNode(EmptyCString(), 0, 0, EmptyCString())
{
}
// nsNavHistoryResult **********************************************************
@ -3788,9 +3878,16 @@ NS_IMETHODIMP nsNavHistoryResult::GetRowProperties(PRInt32 row,
if (row < 0 || row >= PRInt32(mVisibleElements.Length()))
return NS_ERROR_INVALID_ARG;
nsNavHistoryResultNode *node = mVisibleElements[row];
// Add the container property if it's applicable for this row.
if (node->IsContainer()) {
properties->AppendElement(nsNavHistory::sContainerAtom);
}
// Next handle properties for session information.
if (! mShowSessions)
return NS_OK; // don't need to bother to compute session boundaries
nsNavHistoryResultNode *node = mVisibleElements[row];
if (! node->IsVisit())
return NS_OK; // not a visit, so there are no sessions
@ -3909,13 +4006,13 @@ NS_IMETHODIMP nsNavHistoryResult::IsContainerEmpty(PRInt32 row, PRBool *_retval)
// nsNavHistoryResult::IsSeparator (nsITreeView)
//
// We don't support separators
// FIXME: do bookmark separators
NS_IMETHODIMP nsNavHistoryResult::IsSeparator(PRInt32 row, PRBool *_retval)
{
*_retval = PR_FALSE;
if (row < 0 || row >= PRInt32(mVisibleElements.Length()))
return NS_ERROR_INVALID_ARG;
*_retval = mVisibleElements[row]->IsSeparator();
return NS_OK;
}
@ -4022,7 +4119,9 @@ NS_IMETHODIMP nsNavHistoryResult::GetImageSrc(PRInt32 row, nsITreeColumn *col,
// Containers may or may not have favicons. If not, we will return nothing
// as the image, and the style rule should pick up the default.
if (node->IsContainer() && node->mFaviconURI.IsEmpty()) {
// Separator rows never have icons.
if (node->IsSeparator() ||
(node->IsContainer() && node->mFaviconURI.IsEmpty())) {
_retval.Truncate(0);
return NS_OK;
}
@ -4079,7 +4178,9 @@ NS_IMETHODIMP nsNavHistoryResult::GetCellText(PRInt32 row,
// items in the tree view so return a special string if the title is
// empty. Do it here so that callers can still get at the 0 length title
// if they go through the "result" API.
if (! node->mTitle.IsEmpty()) {
if (node->IsSeparator()) {
_retval.Truncate(0);
} else if (! node->mTitle.IsEmpty()) {
_retval = NS_ConvertUTF8toUTF16(node->mTitle);
} else {
nsXPIDLString value;
@ -4114,7 +4215,11 @@ NS_IMETHODIMP nsNavHistoryResult::GetCellText(PRInt32 row,
}
case Column_VisitCount:
{
_retval = NS_ConvertUTF8toUTF16(nsPrintfCString("%d", node->mAccessCount));
if (node->IsSeparator()) {
_retval.Truncate(0);
} else {
_retval = NS_ConvertUTF8toUTF16(nsPrintfCString("%d", node->mAccessCount));
}
break;
}
default:
@ -4706,6 +4811,30 @@ nsNavHistoryResult::OnFolderChanged(PRInt64 aFolder,
}
// nsNavHistoryResult::OnSeparatorAdded (nsINavBookmarkObserver)
NS_IMETHODIMP
nsNavHistoryResult::OnSeparatorAdded(PRInt64 aParent, PRInt32 aIndex)
{
// Separators only appear in folder nodes, so history observers don't care.
ENUMERATE_BOOKMARK_OBSERVERS_FOR_FOLDER(aParent,
OnSeparatorAdded(aParent, aIndex));
return NS_OK;
}
// nsNavHistoryResult::OnSeparatorRemoved (nsINavBookmarkObserver)
NS_IMETHODIMP
nsNavHistoryResult::OnSeparatorRemoved(PRInt64 aParent, PRInt32 aIndex)
{
// Separators only appear in folder nodes, so history observers don't care.
ENUMERATE_BOOKMARK_OBSERVERS_FOR_FOLDER(aParent,
OnSeparatorRemoved(aParent, aIndex));
return NS_OK;
}
// nsNavHistoryResult::OnVisit (nsINavHistoryObserver)
NS_IMETHODIMP

Просмотреть файл

@ -239,7 +239,11 @@ public:
GetType(&type);
return IsTypeQuery(type);
}
PRBool IsSeparator() {
PRUint32 type;
GetType(&type);
return (type == nsINavHistoryResultNode::RESULT_TYPE_SEPARATOR);
}
nsNavHistoryContainerResultNode* GetAsContainer() {
NS_ASSERTION(IsContainer(), "Not a container");
return NS_REINTERPRET_CAST(nsNavHistoryContainerResultNode*, this);
@ -431,7 +435,8 @@ public:
typedef nsCOMArray<nsNavHistoryResultNode>::nsCOMArrayComparatorFunc SortComparator;
virtual PRUint32 GetSortType();
static SortComparator GetSortingComparator(PRUint32 aSortType);
void RecursiveSort(nsICollation* aCollation, SortComparator aComparator);
virtual void RecursiveSort(nsICollation* aCollation,
SortComparator aComparator);
PRUint32 FindInsertionPoint(nsNavHistoryResultNode* aNode, SortComparator aComparator);
PRBool DoesChildNeedResorting(PRUint32 aIndex, SortComparator aComparator);
@ -589,6 +594,10 @@ public:
virtual void OnRemoving();
// Override the sorting implementation to remove separators if we are sorted.
virtual void RecursiveSort(nsICollation* aCollation,
SortComparator aComparator);
public:
// this indicates whether the folder contents are valid, they don't go away
@ -604,6 +613,17 @@ public:
PRBool StartIncrementalUpdate();
};
// nsNavHistorySeparatorResultNode
//
// Separator result nodes do not hold any data.
class nsNavHistorySeparatorResultNode : public nsNavHistoryResultNode
{
public:
nsNavHistorySeparatorResultNode();
NS_IMETHOD GetType(PRUint32* type)
{ *type = nsNavHistoryResultNode::RESULT_TYPE_SEPARATOR; return NS_OK; }
};
// nsNavHistoryResult
//