Bug 387749 - add an item detail pane to the organizer. r=dietrich (r=gavin for editable-menulist changes). a=mconnor.

This commit is contained in:
mozilla.mano@sent.com 2007-09-21 15:57:18 -07:00
parent fadc2450b5
commit 1b9bbe3b58
17 changed files with 666 additions and 259 deletions

View File

@ -139,7 +139,8 @@ var PlacesCommandHook = {
this.panel.openPopup(aAnchorElement, aPosition, -1, -1);
gEditItemOverlay.initPanel(aItemId,
{ hiddenRows: ["description", "location"] });
{ hiddenRows: ["description", "location",
"loadInSidebar"] });
setTimeout(function() {
var namePicker = document.getElementById("editBMPanel_namePicker");
namePicker.focus();

View File

@ -54,8 +54,8 @@ var gEditItemOverlay = {
_uri: null,
_itemId: -1,
_itemType: -1,
_readOnly: false,
_microsummaries: null,
_doneCallback: null,
_hiddenRows: [],
_observersAdded: false,
@ -76,46 +76,83 @@ var gEditItemOverlay = {
_showHideRows: function EIO__showHideRows() {
var isBookmark = this._itemType == Ci.nsINavBookmarksService.TYPE_BOOKMARK;
this._element("nameRow").hidden = this._hiddenRows.indexOf("name") != -1;
this._element("folderRow").hidden =
this._element("nameRow").collapsed = this._hiddenRows.indexOf("name") != -1;
this._element("folderRow").collapsed =
this._hiddenRows.indexOf("folderPicker") != -1;
this._element("tagsRow").hidden =
this._hiddenRows.indexOf("tags") != -1 || !isBookmark;
this._element("descriptionRow").hidden =
this._hiddenRows.indexOf("description") != -1;
this._element("locationRow").hidden =
this._hiddenRows.indexOf("location") != -1 || !isBookmark;
this._element("tagsRow").collapsed = !isBookmark ||
this._hiddenRows.indexOf("tags") != -1;
this._element("descriptionRow").collapsed =
this._hiddenRows.indexOf("description") != -1 ||
this._readOnly;
this._element("keywordRow").collapsed = !isBookmark || this._readOnly ||
this._hiddenRows.indexOf("keyword") != -1;
this._element("locationRow").collapsed = !isBookmark ||
this._hiddenRows.indexOf("location") != -1;
this._element("loadInSidebarCheckbox").collapsed = !isBookmark ||
this._readOnly || this._hiddenRows.indexOf("loadInSidebar") != -1;
this._element("feedLocationRow").collapsed = !this._isLivemark ||
this._hiddenRows.indexOf("feedLocation") != -1;
this._element("siteLocationRow").collapsed = !this._isLivemark ||
this._hiddenRows.indexOf("siteLocation") != -1;
},
/**
* Initialize the panel
*/
initPanel: function EIO_initPanel(aItemId, aInfo) {
const bms = PlacesUtils.bookmarks;
this._folderMenuList = this._element("folderMenuList");
this._folderTree = this._element("folderTree");
this._itemId = aItemId;
this._itemType = PlacesUtils.bookmarks.getItemType(this._itemId);
this._itemType = bms.getItemType(this._itemId);
this._determineInfo(aInfo);
var container = bms.getFolderIdForItem(this._itemId);
if (this._itemType == Ci.nsINavBookmarksService.TYPE_BOOKMARK) {
this._uri = PlacesUtils.bookmarks.getBookmarkURI(this._itemId);
// tags field
this._element("tagsField").value =
PlacesUtils.tagging.getTagsForURI(this._uri).join(", ");
this._uri = bms.getBookmarkURI(this._itemId);
this._isLivemark = false;
if (PlacesUtils.livemarks.isLivemark(container))
this._readOnly = true;
else
this._readOnly = false;
this._element("locationField").value = this._uri.spec;
this._initTextField("locationField", this._uri.spec);
this._initTextField("tagsField",
PlacesUtils.tagging
.getTagsForURI(this._uri).join(", "),
false);
this._initTextField("keywordField",
bms.getKeywordForBookmark(this._itemId));
// Load In Sidebar checkbox
this._element("loadInSidebarCheckbox").checked =
PlacesUtils.annotations.itemHasAnnotation(this._itemId,
LOAD_IN_SIDEBAR_ANNO);
}
else {
this._readOnly = false;
this._isLivemark = PlacesUtils.livemarks.isLivemark(this._itemId);
if (this._isLivemark) {
var feedURI = PlacesUtils.livemarks.getFeedURI(this._itemId);
var siteURI = PlacesUtils.livemarks.getSiteURI(this._itemId);
this._initTextField("feedLocationField", feedURI.spec);
this._initTextField("siteLocationField", siteURI ? siteURI.spec : "");
}
this._uri = null;
}
// folder picker
this._initFolderMenuList();
this._initFolderMenuList(container);
// name picker
this._initNamePicker();
// description field
this._element("descriptionField").value =
PlacesUtils.getItemDescription(this._itemId);
this._initTextField("descriptionField",
PlacesUtils.getItemDescription(this._itemId));
this._showHideRows();
// observe changes
@ -126,6 +163,20 @@ var gEditItemOverlay = {
}
},
_initTextField: function(aTextFieldId, aValue, aReadOnly) {
var field = this._element(aTextFieldId);
field.readOnly = aReadOnly !== undefined ? aReadOnly : this._readOnly;
if (field.value != aValue) {
field.value = aValue;
// clear the undo stack
var editor = field.editor;
if (editor)
editor.transactionManager.clear();
}
},
/**
* Appends a menu-item representing a bookmarks folder to a menu-popup.
* @param aMenupopup
@ -148,16 +199,15 @@ var gEditItemOverlay = {
return folderMenuItem;
},
_initFolderMenuList: function EIO__initFolderMenuList() {
_initFolderMenuList: function EIO__initFolderMenuList(aSelectedFolder) {
// clean up first
var menupopup = this._folderMenuList.menupopup;
while (menupopup.childNodes.length > 4)
menupopup.removeChild(menupopup.lastChild);
var container = PlacesUtils.bookmarks.getFolderIdForItem(this._itemId);
// only show "All Bookmarks" if the url isn't bookmarked somewhere else
this._element("unfiledRootItem").hidden = container != PlacesUtils.unfiledRootId;
this._element("unfiledRootItem").hidden =
aSelectedFolder != PlacesUtils.unfiledRootId;
// List of recently used folders:
var annos = PlacesUtils.annotations;
@ -189,11 +239,12 @@ var gEditItemOverlay = {
this._appendFolderItemToMenupopup(menupopup, folders[i].folderId);
}
var defaultItem = this._getFolderMenuItem(container, true);
var defaultItem = this._getFolderMenuItem(aSelectedFolder, true);
this._folderMenuList.selectedItem = defaultItem;
// Hide the folders-separator if no folder is annotated as recently-used
this._element("foldersSeparator").hidden = (menupopup.childNodes.length <= 4);
this._folderMenuList.disabled = this._readOnly;
},
QueryInterface: function EIO_QueryInterface(aIID) {
@ -265,7 +316,8 @@ var gEditItemOverlay = {
var itemToSelect = userEnteredNameField;
try {
if (this._itemType == Ci.nsINavBookmarksService.TYPE_BOOKMARK)
if (this._itemType == Ci.nsINavBookmarksService.TYPE_BOOKMARK &&
!this._readOnly)
this._microsummaries = this._mss.getMicrosummaries(this._uri, -1);
}
catch(ex) {
@ -303,6 +355,12 @@ var gEditItemOverlay = {
namePicker.selectedItem = itemToSelect;
namePicker.setAttribute("droppable", droppable);
namePicker.readOnly = this._readOnly;
// clear the undo stack
var editor = namePicker.editor;
if (editor)
editor.transactionManager.clear();
},
// nsIMicrosummaryObserver
@ -455,6 +513,52 @@ var gEditItemOverlay = {
}
},
onKeywordFieldBlur: function EIO_onKeywordFieldBlur() {
var keyword = this._element("keywordField").value;
if (keyword != PlacesUtils.bookmarks.getKeywordForBookmark(this._itemId)) {
var txn = PlacesUtils.ptm.editBookmarkKeyword(this._itemId, keyword);
PlacesUtils.ptm.commitTransaction(txn);
}
},
onFeedLocationFieldBlur: function EIO_onFeedLocationFieldBlur() {
// XXXmano: uri fixup
var uri;
try {
uri = IO.newURI(this._element("feedLocationField").value);
}
catch(ex) { return; }
var currentFeedURI = PlacesUtils.livemarks.getFeedURI(this._itemId);
if (!currentFeedURI.equals(uri)) {
var txn = PlacesUtils.ptm.editLivemarkFeedURI(this._itemId, uri);
PlacesUtils.ptm.commitTransaction(txn);
}
},
onSiteLocationFieldBlur: function EIO_onSiteLocationFieldBlur() {
// XXXmano: uri fixup
var uri = null;
try {
uri = IO.newURI(this._element("siteLocationField").value);
}
catch(ex) { }
var currentSiteURI = PlacesUtils.livemarks.getSiteURI(this._itemId);
if (!uri || !currentSiteURI.equals(uri)) {
var txn = PlacesUtils.ptm.editLivemarkSiteURI(this._itemId, uri);
PlacesUtils.ptm.commitTransaction(txn);
}
},
onLoadInSidebarCheckboxCommand:
function EIO_onLoadInSidebarCheckboxCommand() {
var loadInSidebarChecked = this._element("loadInSidebarCheckbox").checked;
var txn = PlacesUtils.ptm.setLoadInSidebar(this._itemId,
loadInSidebarChecked);
PlacesUtils.ptm.commitTransaction(txn);
},
toggleFolderTreeVisibility: function EIO_toggleFolderTreeVisibility() {
var expander = this._element("foldersExpander");
if (!this._folderTree.collapsed) {
@ -670,26 +774,53 @@ var gEditItemOverlay = {
var userEnteredNameField = this._element("userEnteredName");
if (userEnteredNameField.value != aValue) {
userEnteredNameField.value = aValue;
userEnteredNameField.value = aValue;
var namePicker = this._element("namePicker");
if (namePicker.selectedItem == userEnteredNameField)
if (namePicker.selectedItem == userEnteredNameField) {
namePicker.label = aValue;
// clear undo stack
namePicker.editor.transactionManager.clear();
}
}
break;
case "uri":
var locationField = this._element("locationField");
if (locationField.value != aValue) {
locationField.value = aValue;
this._uri = IO.newURI(aValue);
this._initTextField("locationField", this._uri.spec);
this._initNamePicker(); // for microsummaries
this._element("tagsField").value =
PlacesUtils.tagging.getTagsForURI(this._uri).join(", ");
this._initTextField("tagsField",
PlacesUtils.tagging
.getTagsForURI(this._uri).join(", "),
false);
this._rebuildTagsSelectorList();
}
break;
case "keyword":
this._initTextField("keywordField",
PlacesUtils.bookmarks
.getKeywordForBookmark(this._itemId));
break;
case DESCRIPTION_ANNO:
this._element("descriptionField").value =
PlacesUtils.annotations.getItemDescription(this._itemId);
this._initTextField("descriptionField",
PlacesUtils.getItemDescription(this._itemId));
break;
case LOAD_IN_SIDEBAR_ANNO:
this._element("loadInSidebarCheckbox").checked =
PlacesUtils.annotations.itemHasAnnotation(this._itemId,
LOAD_IN_SIDEBAR_ANNO);
break;
case LMANNO_FEEDURI:
var feedURISpec = PlacesUtils.livemarks.getFeedURI(this._itemId).spec;
this._initTextField("feedLocationField", feedURISpec);
break;
case LMANNO_SITEURI:
var siteURISpec = "";
var siteURI = PlacesUtils.livemarks.getSiteURI(this._itemId);
if (siteURI)
siteURISpec = siteURI.spec;
this._initTextField("siteLocationField", siteURISpec);
break;
}
},

View File

@ -49,6 +49,8 @@
src="chrome://browser/content/places/editBookmarkOverlay.js"/>
<vbox id="editBookmarkPanelContent">
<broadcaster id="paneElementsBroadcaster"/>
<grid id="editBookmarkPanelGrid" flex="1">
<columns>
<column/>
@ -57,14 +59,16 @@
<rows>
<row align="center" id="editBMPanel_nameRow">
<label value="&editBookmarkOverlay.name.label;"
contorl="editBMPanel_namePicker"/>
control="editBMPanel_namePicker"
observes="paneElementsBroadcaster"/>
<menulist id="editBMPanel_namePicker"
flex="1"
editable="true"
droppable="false"
oninput="gEditItemOverlay.onNamePickerInput();"
onblur="gEditItemOverlay.onNamePickerChange();"
oncommand="gEditItemOverlay.onNamePickerChange();">
oncommand="gEditItemOverlay.onNamePickerChange();"
observes="paneElementsBroadcaster">
<menupopup>
<menuitem id="editBMPanel_userEnteredName"/>
<menuitem disabled="true">
@ -77,17 +81,39 @@
<row align="center" id="editBMPanel_locationRow">
<label value="&editBookmarkOverlay.location.label;"
contorl="editBMPanel_locationField"/>
control="editBMPanel_locationField"
observes="paneElementsBroadcaster"/>
<textbox id="editBMPanel_locationField"
onblur="gEditItemOverlay.onLocationFieldBlur();"/>
onblur="gEditItemOverlay.onLocationFieldBlur();"
observes="paneElementsBroadcaster"/>
</row>
<row align="center" id="editBMPanel_feedLocationRow">
<label value="&editBookmarkOverlay.feedLocation.label;"
control="editBMPanel_feedLocationField"
observes="paneElementsBroadcaster"/>
<textbox id="editBMPanel_feedLocationField"
onblur="gEditItemOverlay.onFeedLocationFieldBlur();"
observes="paneElementsBroadcaster"/>
</row>
<row align="center" id="editBMPanel_siteLocationRow">
<label value="&editBookmarkOverlay.siteLocation.label;"
control="editBMPanel_siteLocationField"
observes="paneElementsBroadcaster"/>
<textbox id="editBMPanel_siteLocationField"
onblur="gEditItemOverlay.onSiteLocationFieldBlur();"
observes="paneElementsBroadcaster"/>
</row>
<row align="center" id="editBMPanel_folderRow">
<label value="&editBookmarkOverlay.folder.label;"
control="editBMPanel_folderMenuList"/>
control="editBMPanel_folderMenuList"
observes="paneElementsBroadcaster"/>
<menulist id="editBMPanel_folderMenuList"
class="folder-icon"
oncommand="gEditItemOverlay.onFolderMenuListCommand();">
oncommand="gEditItemOverlay.onFolderMenuListCommand();"
observes="paneElementsBroadcaster">
<menupopup>
<!-- Static item for special folders -->
<menuitem id="editBMPanel_unfiledRootItem"
@ -107,7 +133,8 @@
tooltiptext="&editBookmarkOverlay.foldersExpanderDown.tooltip;"
tooltiptextdown="&editBookmarkOverlay.foldersExpanderDown.tooltip;"
tooltiptextup="&editBookmarkOverlay.expanderUp.tooltip;"
oncommand="gEditItemOverlay.toggleFolderTreeVisibility();"/>
oncommand="gEditItemOverlay.toggleFolderTreeVisibility();"
observes="paneElementsBroadcaster"/>
</row>
<tree id="editBMPanel_folderTree"
@ -118,7 +145,8 @@
onselect="gEditItemOverlay.onFolderTreeSelect();"
showRoot="true"
place="place:folder=2&amp;group=3&amp;excludeItems=1&amp;excludeQueries=1&amp;excludeReadOnlyFolders=1"
hidecolumnpicker="true">
hidecolumnpicker="true"
observes="paneElementsBroadcaster">
<treecols>
<treecol anonid="title" flex="1" primary="true" hideheader="true"/>
</treecols>
@ -127,25 +155,46 @@
<row align="center" id="editBMPanel_tagsRow">
<label value="&editBookmarkOverlay.tags.label;"
control="tagsField"/>
control="tagsField"
observes="paneElementsBroadcaster"/>
<textbox id="editBMPanel_tagsField"
onblur="gEditItemOverlay.onTagsFieldBlur();"/>
onblur="gEditItemOverlay.onTagsFieldBlur();"
observes="paneElementsBroadcaster"/>
<button id="editBMPanel_tagsSelectorExpander"
class="expander-down"
tooltiptext="&editBookmarkOverlay.tagsExpanderDown.tooltip;"
tooltiptextdown="&editBookmarkOverlay.tagsExpanderDown.tooltip;"
tooltiptextup="&editBookmarkOverlay.expanderUp.tooltip;"
oncommand="gEditItemOverlay.toggleTagsSelector();"/>
oncommand="gEditItemOverlay.toggleTagsSelector();"
observes="paneElementsBroadcaster"/>
</row>
<listbox id="editBMPanel_tagsSelector" height="150" collapsed="true"/>
<listbox id="editBMPanel_tagsSelector"
height="150" collapsed="true"
observes="paneElementsBroadcaster"/>
<row id="editBMPanel_descriptionRow" align="center">
<row id="editBMPanel_keywordRow">
<label value="&editBookmarkOverlay.keyword.label;"
control="editBMPanel_keywordField"
observes="paneElementsBroadcaster"/>
<textbox id="editBMPanel_keywordField"
onblur="gEditItemOverlay.onKeywordFieldBlur();"
observes="paneElementsBroadcaster"/>
</row>
<row id="editBMPanel_descriptionRow">
<label value="&editBookmarkOverlay.description.label;"
control="editBMPanel_descriptionField"/>
control="editBMPanel_descriptionField"
observes="paneElementsBroadcaster"/>
<textbox id="editBMPanel_descriptionField"
onblur="gEditItemOverlay.onDescriptionFieldBlur();"/>
multiline="true"
onblur="gEditItemOverlay.onDescriptionFieldBlur();"
observes="paneElementsBroadcaster"/>
</row>
<checkbox id="editBMPanel_loadInSidebarCheckbox"
label="&editBookmarkOverlay.loadInSidebar.label;"
oncommand="gEditItemOverlay.onLoadInSidebarCheckboxCommand();"
observes="paneElementsBroadcaster"/>
</rows>
</grid>
</vbox>

View File

@ -27,3 +27,10 @@ button.commandButton {
display: -moz-box;
}
/* Edit Item Panel */
#infoScrollbox[minimal="true"] #editBMPanel_descriptionRow,
#infoScrollbox[minimal="true"] #editBMPanel_loadInSidebarCheckbox,
#infoScrollbox[minimal="true"] #editBMPanel_keywordRow {
visibility: collapse;
}

View File

@ -10,4 +10,3 @@ hbox[type="places"] {
menupopup[type="places"] {
-moz-binding: url("chrome://browser/content/places/menu.xml#places-menupopup");
}

View File

@ -40,8 +40,8 @@
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
/**
* Selects a place URI in the places list.
* This function is global so it can be easily accessed by openers.
* Selects a place URI in the places list.
* This function is global so it can be easily accessed by openers.
* @param placeURI
* A place: URI string to select
*/
@ -59,7 +59,7 @@ var PlacesOrganizer = {
// in the places tree binding's constructor
setTimeout(function() { self._init(); }, 0);
},
_init: function PO__init() {
this._places = document.getElementById("placesList");
this._content = document.getElementById("placeContent");
@ -106,6 +106,7 @@ var PlacesOrganizer = {
this._backHistory.unshift(this.location);
this._content.place = this._location = aLocation;
this.onContentTreeSelect();
// update navigation commands
if (this._backHistory.length == 0)
@ -138,7 +139,7 @@ var PlacesOrganizer = {
HEADER_TYPE_ADVANCED_SEARCH: 3,
/**
* Updates the text shown in the heading banner above the content view.
* Updates the text shown in the heading banner above the content view.
* @param type
* The type of information being shown - normal (built-in history or
* other query, bookmark folder), search results from the toolbar
@ -155,7 +156,7 @@ var PlacesOrganizer = {
var contentTitle = document.getElementById("contentTitle");
contentTitle.setAttribute("value", text);
// Hide the advanced search controls when the user hasn't searched
var searchModifiers = document.getElementById("searchModifiers");
searchModifiers.hidden = type == this.HEADER_TYPE_SHOWING;
@ -182,23 +183,22 @@ var PlacesOrganizer = {
},
/**
* Loads the place URI entered in the debug
* Loads the place URI entered in the debug
*/
loadPlaceURI: function PO_loadPlaceURI() {
// clear forward history
this._forwardHistory.splice(0);
var placeURI = document.getElementById("placeURI");
var queriesRef = { }, optionsRef = { };
PlacesUtils.history.queryStringToQueries(placeURI.value,
PlacesUtils.history.queryStringToQueries(placeURI.value,
queriesRef, { }, optionsRef);
// for debug panel
var autoFilterResults = document.getElementById("autoFilterResults");
if (autoFilterResults.checked) {
var options =
var options =
OptionsFilter.filter(queriesRef.value, optionsRef.value, null);
}
else
@ -208,7 +208,7 @@ var PlacesOrganizer = {
PlacesUtils.history.queriesToQueryString(queriesRef.value,
queriesRef.value.length,
options);
placeURI.value = this.location;
this.setHeaderText(this.HEADER_TYPE_SHOWING, "Debug results for: " + placeURI.value);
@ -227,7 +227,7 @@ var PlacesOrganizer = {
var options = queryNode.queryOptions;
var loadedURI = document.getElementById("loadedURI");
loadedURI.value =
PlacesUtils.history.queriesToQueryString(queries, queries.length,
PlacesUtils.history.queriesToQueryString(queries, queries.length,
options);
},
@ -254,7 +254,7 @@ var PlacesOrganizer = {
// update location
this.location = PlacesUtils.history.queriesToQueryString(queries, queries.length, options);
// Make sure the query builder is hidden.
PlacesQueryBuilder.hide();
if (resetSearchBox) {
@ -277,9 +277,9 @@ var PlacesOrganizer = {
},
/**
* Handle clicks on the tree. If the user middle clicks on a URL, load that
* URL according to rules. Single clicks or modified clicks do not result in
* any special action, since they're related to selection.
* Handle clicks on the tree. If the user middle clicks on a URL, load that
* URL according to rules. Single clicks or modified clicks do not result in
* any special action, since they're related to selection.
* @param aEvent
* The mouse event.
*/
@ -287,7 +287,7 @@ var PlacesOrganizer = {
var currentView = aEvent.currentTarget;
var controller = currentView.controller;
// If the user clicked on a tree column header, update the sorting
// If the user clicked on a tree column header, update the sorting
// preferences to reflect their choices.
// XXXmano: should be done in tree.xml
if (aEvent.target.localName == "treecol") {
@ -298,9 +298,9 @@ var PlacesOrganizer = {
if (PlacesUtils.nodeIsURI(currentView.selectedNode))
controller.openSelectedNodeWithEvent(aEvent);
else if (PlacesUtils.nodeIsContainer(currentView.selectedNode)) {
// The command execution function will take care of seeing the
// selection is a folder/container and loading its contents in
// tabs for us.
// The command execution function will take care of seeing the
// selection is a folder/container and loading its contents in
// tabs for us.
controller.openLinksInTabs();
}
}
@ -313,7 +313,7 @@ var PlacesOrganizer = {
getCurrentOptions: function PO_getCurrentOptions() {
return asQuery(this._content.getResult().root).queryOptions;
},
/**
* Show the migration wizard for importing from a file.
*/
@ -379,9 +379,9 @@ var PlacesOrganizer = {
// get bookmarks backup dir
var dirSvc = Cc["@mozilla.org/file/directory_service;1"].
getService(Ci.nsIProperties);
getService(Ci.nsIProperties);
var bookmarksBackupDir = dirSvc.get("ProfD", Ci.nsIFile);
bookmarksBackupDir.append("bookmarkbackups");
bookmarksBackupDir.append("bookmarkbackups");
if (!bookmarksBackupDir.exists())
return; // no backup files
@ -424,22 +424,22 @@ var PlacesOrganizer = {
*/
onRestoreMenuItemClick: function PO_onRestoreMenuItemClick(aMenuItem) {
var dirSvc = Cc["@mozilla.org/file/directory_service;1"].
getService(Ci.nsIProperties);
getService(Ci.nsIProperties);
var bookmarksFile = dirSvc.get("ProfD", Ci.nsIFile);
bookmarksFile.append("bookmarkbackups");
bookmarksFile.append("bookmarkbackups");
bookmarksFile.append(aMenuItem.getAttribute("value"));
if (!bookmarksFile.exists())
return;
var prompts = Cc["@mozilla.org/embedcomp/prompt-service;1"].
getService(Ci.nsIPromptService);
getService(Ci.nsIPromptService);
if (!prompts.confirm(null,
PlacesUtils.getString("bookmarksRestoreAlertTitle"),
PlacesUtils.getString("bookmarksRestoreAlert")))
return;
var ieSvc = Cc["@mozilla.org/browser/places/import-export-service;1"].
getService(Ci.nsIPlacesImportExportService);
getService(Ci.nsIPlacesImportExportService);
ieSvc.importHTMLFromFile(bookmarksFile, true);
},
@ -451,9 +451,9 @@ var PlacesOrganizer = {
fp.init(window, PlacesUtils.getString("bookmarksBackupTitle"),
Ci.nsIFilePicker.modeSave);
fp.appendFilters(Ci.nsIFilePicker.filterHTML);
var dirSvc = Cc["@mozilla.org/file/directory_service;1"].
getService(Ci.nsIProperties);
getService(Ci.nsIProperties);
var backupsDir = dirSvc.get("Desk", Ci.nsILocalFile);
fp.displayDirectory = backupsDir;
@ -462,10 +462,10 @@ var PlacesOrganizer = {
var date = (new Date).toLocaleFormat("%Y-%m-%d");
fp.defaultString = PlacesUtils.getFormattedString("bookmarksBackupFilename",
[date]);
if (fp.show() != Ci.nsIFilePicker.returnCancel) {
var ieSvc = Cc["@mozilla.org/browser/places/import-export-service;1"].
getService(Ci.nsIPlacesImportExportService);
getService(Ci.nsIPlacesImportExportService);
ieSvc.exportHTMLToFile(fp.file);
}
},
@ -476,50 +476,181 @@ var PlacesOrganizer = {
*/
restoreFromFile: function PO_restoreFromFile() {
var prompts = Cc["@mozilla.org/embedcomp/prompt-service;1"].
getService(Ci.nsIPromptService);
getService(Ci.nsIPromptService);
if (!prompts.confirm(null, PlacesUtils.getString("bookmarksRestoreAlertTitle"),
PlacesUtils.getString("bookmarksRestoreAlert")))
return;
var fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
fp.init(window, PlacesUtils.getString("bookmarksRestoreTitle"),
Ci.nsIFilePicker.modeOpen);
fp.appendFilters(Ci.nsIFilePicker.filterHTML);
var dirSvc = Cc["@mozilla.org/file/directory_service;1"].
getService(Ci.nsIProperties);
getService(Ci.nsIProperties);
var backupsDir = dirSvc.get("Desk", Ci.nsILocalFile);
fp.displayDirectory = backupsDir;
if (fp.show() != Ci.nsIFilePicker.returnCancel) {
var ieSvc = Cc["@mozilla.org/browser/places/import-export-service;1"].
getService(Ci.nsIPlacesImportExportService);
getService(Ci.nsIPlacesImportExportService);
ieSvc.importHTMLFromFile(fp.file, true);
}
},
_paneDisabled: false,
_setDetailsFieldsDisabledState:
function PO__setDetailsFieldsDisabledState(aDisabled) {
if (aDisabled) {
document.getElementById("paneElementsBroadcaster")
.setAttribute("disabled", "true");
}
else {
document.getElementById("paneElementsBroadcaster")
.removeAttribute("disabled");
}
},
_detectAndSetDetailsPaneMinimalState:
function PO__detectAndSetDetailsPaneMinimalState(aNode) {
/**
* The details of simple folder-items (as opposed to livemarks) or the
* of livemark-children are not likely to fill the scrollbox anyway,
* thus we remove the "More/Less" button and show all details.
*
* the wasminimal attribute here is used to persist the "more/less"
* state in a bookmark->folder->bookmark scenario.
*/
var infoScrollbox = document.getElementById("infoScrollbox");
var scrollboxExpander = document.getElementById("infoScrollboxExpander");
if ((PlacesUtils.nodeIsFolder(aNode) &&
!PlacesUtils.nodeIsLivemarkContainer(aNode)) ||
PlacesUtils.nodeIsLivemarkItem(aNode)) {
if (infoScrollbox.getAttribute("minimal") == "true")
infoScrollbox.setAttribute("wasminimal", "true");
infoScrollbox.removeAttribute("minimal");
scrollboxExpander.hidden = true;
}
else {
if (infoScrollbox.getAttribute("wasminimal") == "true")
infoScrollbox.setAttribute("minimal", "true");
infoScrollbox.removeAttribute("wasminimal");
scrollboxExpander.hidden = false;
}
},
updateThumbnailProportions: function PO_updateThumbnailProportions() {
var previewBox = document.getElementById("previewBox");
var canvas = document.getElementById("itemThumbnail");
var height = previewBox.boxObject.height;
var width = height * (screen.width / screen.height);
canvas.width = width;
canvas.height = height;
},
onContentTreeSelect: function PO_onContentTreeSelect() {
// If a textbox within a panel is focused, force-blur it so its contents
// are saved
if (gEditItemOverlay.itemId != -1) {
var focusedElement = document.commandDispatcher.focusedElement;
if (focusedElement instanceof HTMLInputElement &&
/^editBMPanel.*/.test(focusedElement.parentNode.parentNode.id))
focusedElement.blur();
}
var contentTree = document.getElementById("placeContent");
var deck = document.getElementById("infoDeck");
this.updateStatusBarForView(contentTree);
if (contentTree.hasSingleSelection) {
var selectedNode = contentTree.selectedNode;
if (selectedNode.itemId != -1 &&
!PlacesUtils.nodeIsSeparator(selectedNode)) {
gEditItemOverlay.initPanel(selectedNode.itemId,
{ hiddenRows: ["folderPicker"] });
deck.selectedIndex = 1;
return;
var detailsDeck = document.getElementById("detailsDeck");
if (contentTree.hasSelection) {
detailsDeck.selectedIndex = 1;
if (contentTree.hasSingleSelection) {
var selectedNode = contentTree.selectedNode;
if (selectedNode.itemId != -1 &&
!PlacesUtils.nodeIsSeparator(selectedNode)) {
if (this._paneDisabled) {
this._setDetailsFieldsDisabledState(false);
this._paneDisabled = false;
}
gEditItemOverlay.initPanel(selectedNode.itemId,
{ hiddenRows: ["folderPicker"] });
this._detectAndSetDetailsPaneMinimalState(selectedNode);
this.updateThumbnailProportions();
this._updateThumbnail();
return;
}
}
}
gEditItemOverlay.uninitPanel();
deck.selectedIndex = 0;
else {
detailsDeck.selectedIndex = 0;
var selectItemDesc = document.getElementById("selectItemDescription");
var itemsCountLabel = document.getElementById("itemsCountText");
var rowCount = this._content.treeBoxObject.view.rowCount;
if (rowCount == 0) {
selectItemDesc.hidden = true;
itemsCountLabel.value = PlacesUtils.getString("detailsPane.noItems");
}
else {
selectItemDesc.hidden = false;
if (rowCount == 1)
itemsCountLabel.value = PlacesUtils.getString("detailsPane.oneItem");
else {
itemsCountLabel.value =
PlacesUtils.getFormattedString("detailsPane.multipleItems",
[rowCount]);
}
}
this.updateThumbnailProportions();
this._updateThumbnail();
}
// Nothing to do if the pane was already disabled
if (!this._paneDisabled) {
gEditItemOverlay.uninitPanel();
this._setDetailsFieldsDisabledState(true);
this._paneDisabled = true;
}
},
_updateThumbnail: function PO__updateThumbnail() {
var bo = document.getElementById("previewBox").boxObject;
var width = bo.width;
var height = bo.height;
var canvas = document.getElementById("itemThumbnail");
var ctx = canvas.getContext('2d');
var notAvailableText = canvas.getAttribute("notavailabletext");
ctx.save();
ctx.fillStyle = "-moz-Dialog";
ctx.fillRect(0, 0, width, height);
ctx.translate(width/2, height/2);
ctx.fillStyle = "GrayText";
ctx.mozTextStyle = "12pt sans serif";
var len = ctx.mozMeasureText(notAvailableText);
ctx.translate(-len/2,0);
ctx.mozDrawText(notAvailableText);
ctx.restore();
},
toggleAdditionalInfoFields: function PO_toggleAdditionalInfoFields() {
var infoScrollbox = document.getElementById("infoScrollbox");
var scrollboxExpander = document.getElementById("infoScrollboxExpander");
if (infoScrollbox.getAttribute("minimal") == "true") {
infoScrollbox.removeAttribute("minimal");
scrollboxExpander.label = scrollboxExpander.getAttribute("lesslabel");
}
else {
infoScrollbox.setAttribute("minimal", "true");
scrollboxExpander.label = scrollboxExpander.getAttribute("morelabel");
}
},
/**
* Save the current search (or advanced query) to the bookmarks root.
*/
saveSearch: function PP_saveSearch() {
saveSearch: function PO_saveSearch() {
// Get the place: uri for the query.
// If the advanced query builder is showing, use that.
var queries = [];
@ -553,7 +684,7 @@ var PlacesOrganizer = {
var defaultText = PlacesUtils.getString("saveSearch.defaultText");
var prompts = Cc["@mozilla.org/embedcomp/prompt-service;1"].
getService(Ci.nsIPromptService);
getService(Ci.nsIPromptService);
var check = {value: false};
var input = {value: defaultText};
var save = prompts.prompt(null, title, inputLabel, input, null, check);
@ -562,14 +693,17 @@ var PlacesOrganizer = {
if (!save || input.value == "")
return;
// Add the place: uri as a bookmark under the places root.
var txn = PlacesUtils.ptm.createItem(placeURI, PlacesUtils.bookmarks.bookmarksRoot, PlacesUtils.bookmarks.DEFAULT_INDEX, input.value);
// Add the place: uri as a bookmark under the bookmarks root.
var txn = PlacesUtils.ptm.createItem(placeURI,
PlacesUtils.bookmarksRootId,
PlacesUtils.bookmarks.DEFAULT_INDEX,
input.value);
PlacesUtils.ptm.commitTransaction(txn);
}
};
/**
* A set of utilities relating to search within Bookmarks and History.
* A set of utilities relating to search within Bookmarks and History.
*/
var PlacesSearchBox = {
@ -583,14 +717,14 @@ var PlacesSearchBox = {
/**
* Run a search for the specified text, over the collection specified by
* the dropdown arrow. The default is all bookmarks, but can be
* localized to the active collection.
* localized to the active collection.
* @param filterString
* The text to search for.
* The text to search for.
*/
search: function PSB_search(filterString) {
// for non-"bookmarks" collections,
// for non-"bookmarks" collections,
// do not search for "" since it will match all history. Assume if the user
// deleted everything that they want to type something else and don't
// deleted everything that they want to type something else and don't
// update the view.
if (PlacesSearchBox.filterCollection != "bookmarks" &&
(filterString == "" || this.searchFilter.hasAttribute("empty")))
@ -618,7 +752,7 @@ var PlacesSearchBox = {
PO.setHeaderText(PO.HEADER_TYPE_SEARCH, filterString);
break;
}
this.searchFilter.setAttribute("filtered", "true");
},
@ -645,7 +779,7 @@ var PlacesSearchBox = {
*/
updateCollectionTitle: function PSB_updateCollectionTitle(title) {
if (title) {
this.searchFilter.grayText =
this.searchFilter.grayText =
PlacesUtils.getFormattedString("searchCurrentDefault", [title]);
}
else
@ -685,17 +819,17 @@ var PlacesSearchBox = {
this.searchFilter.focus();
},
/**
* Set up the gray text in the search bar as the Places View loads.
/**
* Set up the gray text in the search bar as the Places View loads.
*/
init: function PSB_init() {
var searchFilter = this.searchFilter;
searchFilter.grayText = PlacesUtils.getString("searchByDefault");
searchFilter.reset();
},
/**
* Gets or sets the text shown in the Places Search Box
* Gets or sets the text shown in the Places Search Box
*/
get value() {
return this.searchFilter.value;
@ -716,8 +850,8 @@ var PlacesQueryBuilder = {
_numRows: 0,
/**
* The maximum number of terms that can be added.
* XXXben - this should be generated dynamically based on the contents of a
* The maximum number of terms that can be added.
* XXXben - this should be generated dynamically based on the contents of a
* list of terms searchable through this widget, rather than being
* a hard coded number.
*/
@ -765,7 +899,7 @@ var PlacesQueryBuilder = {
"visited": this._locationSearch,
"location": null
};
this._queryBuilders = {
"keyword": this.setKeywordQuery,
"visited": this.setVisitedQuery,
@ -787,7 +921,7 @@ var PlacesQueryBuilder = {
var titleDeck = document.getElementById("titleDeck");
titleDeck.setAttribute("selectedIndex", 0);
},
/**
* Shows the query builder
*/
@ -797,7 +931,7 @@ var PlacesQueryBuilder = {
},
/**
* Includes the rowId in the id attribute of an element in a row newly
* Includes the rowId in the id attribute of an element in a row newly
* created from the template row.
* @param element
* The element whose id attribute needs to be updated.
@ -832,7 +966,7 @@ var PlacesQueryBuilder = {
},
/**
* Adds a row to the view, prefilled with the next query subject. If the
* Adds a row to the view, prefilled with the next query subject. If the
* query builder is not visible, it will be shown.
*/
addRow: function PQB_addRow() {
@ -848,7 +982,7 @@ var PlacesQueryBuilder = {
// Determine what the search type is based on the last visible row. If this
// is the first row, the type is "keyword search". Otherwise, it's the next
// in the sequence after the one defined by the previous visible row's
// in the sequence after the one defined by the previous visible row's
// Subject selector, as defined in _nextSearch.
var searchType = this._keywordSearch;
var lastMenu = document.getElementById("advancedSearch" +
@ -857,15 +991,15 @@ var PlacesQueryBuilder = {
if (this._numRows > 0 && lastMenu && lastMenu.selectedItem) {
searchType = this._nextSearch[lastMenu.selectedItem.value];
}
// There is no "next" search type. We are here in error.
// There is no "next" search type. We are here in error.
if (!searchType)
return;
// We don't insert into the document until _after_ the searchType is
// We don't insert into the document until _after_ the searchType is
// determined, since this will interfere with the computation.
gridRows.appendChild(newRow);
this._setRowId(newRow, ++this._numRows);
// Ensure the Advanced Search container is visible, if this is the first
// Ensure the Advanced Search container is visible, if this is the first
// row being added.
var advancedSearch = document.getElementById("advancedSearch");
if (advancedSearch.collapsed) {
@ -874,10 +1008,10 @@ var PlacesQueryBuilder = {
// Update the header.
const asType = PlacesOrganizer.HEADER_TYPE_ADVANCED_SEARCH;
PlacesOrganizer.setHeaderText(asType, "");
// Pre-fill the search terms field with the value from the one on the
// Pre-fill the search terms field with the value from the one on the
// toolbar.
// For some reason, setting.value here synchronously does not appear to
// For some reason, setting.value here synchronously does not appear to
// work.
var searchTermsField = document.getElementById("advancedSearch1Textbox");
if (searchTermsField)
@ -888,7 +1022,7 @@ var PlacesQueryBuilder = {
// the toolbar.
this.addRow();
return;
}
}
this.showSearch(this._numRows, searchType);
this._updateUIForRowChange();
@ -930,7 +1064,7 @@ var PlacesQueryBuilder = {
// there are.
var d0 = null;
var d1 = null;
// If there are an even number of elements in the date array, try to
// If there are an even number of elements in the date array, try to
// parse it as a range of two dates.
if ((dateArr.length & 1) == 0) {
var mid = dateArr.length / 2;
@ -971,11 +1105,11 @@ var PlacesQueryBuilder = {
var calendar = document.getElementById("advancedSearch" + row + "Calendar");
var begin = calendar.beginrange;
var end = calendar.endrange;
// If the calendar doesn't have a begin/end, don't change the textbox.
if (begin == null || end == null)
return true;
// If the begin and end are the same day, only fill that into the textbox.
var textbox = document.getElementById("advancedSearch" + row + "TimePicker");
var beginDate = begin.getDate();
@ -1007,10 +1141,10 @@ var PlacesQueryBuilder = {
endDate);
textbox.value = beginStr + " - " + endStr;
}
// Update the search.
this.doSearch();
return true;
},
@ -1058,7 +1192,7 @@ var PlacesQueryBuilder = {
element.hidden = true;
}
}
this.doSearch();
},
@ -1073,17 +1207,15 @@ var PlacesQueryBuilder = {
}
else {
query.uriIsPrefix = (type == "startswith");
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
var spec = document.getElementById(prefix + "Textbox").value;
try {
query.uri = ios.newURI(spec, null, null);
query.uri = IO.newURI(spec);
}
catch (e) {
// Invalid input can cause newURI to barf, that's OK, tack "http://"
// Invalid input can cause newURI to barf, that's OK, tack "http://"
// onto the front and try again to see if the user omitted it
try {
query.uri = ios.newURI("http://" + spec, null, null);
query.uri = IO.newURI("http://" + spec);
}
catch (e) {
// OK, they have entered something which can never match. This should
@ -1143,7 +1275,7 @@ var PlacesQueryBuilder = {
for (var i = 1; updated < this._numRows; ++i) {
var prefix = "advancedSearch" + i;
// The user can remove rows from the middle and start of the list, not
// The user can remove rows from the middle and start of the list, not
// just from the end, so we need to make sure that this row actually
// exists before attempting to construct a query for it.
var querySubjectElement = document.getElementById(prefix + "Subject");
@ -1155,30 +1287,30 @@ var PlacesQueryBuilder = {
query = this.queries[0];
else
query = PlacesUtils.history.getNewQuery();
var querySubject = querySubjectElement.value;
this._queryBuilders[querySubject](query, prefix);
if (queryType == "or")
this.queries.push(query);
++updated;
}
}
// Make sure we're getting uri results, not visits
this.options = PlacesOrganizer.getCurrentOptions();
this.options.resultType = options.RESULT_TYPE_URI;
// XXXben - find some public way of doing this!
PlacesOrganizer._content.load(queries,
PlacesOrganizer._content.load(queries,
OptionsFilter.filter(queries, options, null));
PlacesOrganizer.updateLoadedURI();
}
};
/**
* Population and commands for the View Menu.
* Population and commands for the View Menu.
*/
var ViewMenu = {
/**
@ -1186,36 +1318,36 @@ var ViewMenu = {
* @param popup
* The popup that contains the previously generated content.
* @param startID
* The id attribute of an element that is the start of the
* The id attribute of an element that is the start of the
* dynamically generated region - remove elements after this
* item only.
* item only.
* Must be contained by popup. Can be null (in which case the
* contents of popup are removed).
* contents of popup are removed).
* @param endID
* The id attribute of an element that is the end of the
* dynamically generated region - remove elements up to this
* item only.
* Must be contained by popup. Can be null (in which case all
* items until the end of the popup will be removed). Ignored
* if startID is null.
* @returns The element for the caller to insert new items before,
* if startID is null.
* @returns The element for the caller to insert new items before,
* null if the caller should just append to the popup.
*/
_clean: function VM__clean(popup, startID, endID) {
if (endID)
if (endID)
NS_ASSERT(startID, "meaningless to have valid endID and null startID");
if (startID) {
var startElement = document.getElementById(startID);
NS_ASSERT(startElement.parentNode ==
NS_ASSERT(startElement.parentNode ==
popup, "startElement is not in popup");
NS_ASSERT(startElement,
NS_ASSERT(startElement,
"startID does not correspond to an existing element");
var endElement = null;
if (endID) {
endElement = document.getElementById(endID);
NS_ASSERT(endElement.parentNode == popup,
NS_ASSERT(endElement.parentNode == popup,
"endElement is not in popup");
NS_ASSERT(endElement,
NS_ASSERT(endElement,
"endID does not correspond to an existing element");
}
while (startElement.nextSibling != endElement)
@ -1224,7 +1356,7 @@ var ViewMenu = {
}
else {
while(popup.hasChildNodes())
popup.removeChild(popup.firstChild);
popup.removeChild(popup.firstChild);
}
return null;
},
@ -1238,8 +1370,8 @@ var ViewMenu = {
* @param endID
* see _clean
* @param type
* the type of the menuitem, e.g. "radio" or "checkbox".
* Can be null (no-type).
* the type of the menuitem, e.g. "radio" or "checkbox".
* Can be null (no-type).
* Checkboxes are checked if the column is visible.
* @param propertyPrefix
* If propertyPrefix is non-null:
@ -1251,11 +1383,11 @@ var ViewMenu = {
* no accesskey is assigned.
*/
fillWithColumns: function VM_fillWithColumns(event, startID, endID, type, propertyPrefix) {
var popup = event.target;
var popup = event.target;
var pivot = this._clean(popup, startID, endID);
// If no column is "sort-active", the "Unsorted" item needs to be checked,
// so track whether or not we find a column that is sort-active.
// If no column is "sort-active", the "Unsorted" item needs to be checked,
// so track whether or not we find a column that is sort-active.
var isSorted = false;
var content = document.getElementById("placeContent");
var columns = content.columns;
@ -1278,7 +1410,7 @@ var ViewMenu = {
if (type == "radio") {
menuitem.setAttribute("type", "radio");
menuitem.setAttribute("name", "columns");
// This column is the sort key. Its item is checked.
// This column is the sort key. Its item is checked.
if (column.getAttribute("sortDirection") != "") {
menuitem.setAttribute("checked", "true");
isSorted = true;
@ -1289,18 +1421,18 @@ var ViewMenu = {
// Cannot uncheck the primary column.
if (column.getAttribute("primary") == "true")
menuitem.setAttribute("disabled", "true");
// Items for visible columns are checked.
// Items for visible columns are checked.
if (!column.hidden)
menuitem.setAttribute("checked", "true");
}
if (pivot)
popup.insertBefore(menuitem, pivot);
else
popup.appendChild(menuitem);
popup.appendChild(menuitem);
}
event.stopPropagation();
},
/**
* Set up the content of the view menu.
*/
@ -1310,7 +1442,7 @@ var ViewMenu = {
var sortColumn = this._getSortColumn();
var viewSortAscending = document.getElementById("viewSortAscending");
var viewSortDescending = document.getElementById("viewSortDescending");
// We need to remove an existing checked attribute because the unsorted
// We need to remove an existing checked attribute because the unsorted
// menu item is not rebuilt every time we open the menu like the others.
var viewUnsorted = document.getElementById("viewUnsorted");
if (!sortColumn) {
@ -1329,7 +1461,7 @@ var ViewMenu = {
viewUnsorted.removeAttribute("checked");
}
},
/**
* Shows/Hides a tree column.
* @param element
@ -1339,13 +1471,13 @@ var ViewMenu = {
const PREFIX = "menucol_";
var columnID = element.id.substr(PREFIX.length, element.id.length);
var column = document.getElementById(columnID);
NS_ASSERT(column,
NS_ASSERT(column,
"menu item for column that doesn't exist?! id = " + element.id);
var splitter = column.nextSibling;
if (splitter && splitter.localName != "splitter")
splitter = null;
if (element.getAttribute("checked") == "true") {
column.removeAttribute("hidden");
if (splitter)
@ -1355,12 +1487,12 @@ var ViewMenu = {
column.setAttribute("hidden", "true");
if (splitter)
splitter.setAttribute("hidden", "true");
}
}
},
/**
* Gets the last column that was sorted.
* @returns the currently sorted column, null if there is no sorted column.
* Gets the last column that was sorted.
* @returns the currently sorted column, null if there is no sorted column.
*/
_getSortColumn: function VM__getSortColumn() {
var content = document.getElementById("placeContent");
@ -1380,7 +1512,7 @@ var ViewMenu = {
* The ID of the colum that is the sort key. Can be null - the
* current sort column id or "title" will be used.
* @param aDirection
* The direction to sort - "ascending" or "descending".
* The direction to sort - "ascending" or "descending".
* Can be null - the last direction or descending will be used.
*
* If both aColumnID and aDirection are null, the view will be unsorted.
@ -1416,7 +1548,7 @@ var ViewMenu = {
case "date":
sortingMode = aDirection == "descending" ?
NHQO.SORT_BY_DATE_DESCENDING : NHQO.SORT_BY_DATE_ASCENDING;
break;
break;
case "visitCount":
sortingMode = aDirection == "descending" ?
NHQO.SORT_BY_VISITCOUNT_DESCENDING : NHQO.SORT_BY_VISITCOUNT_ASCENDING;
@ -1448,12 +1580,12 @@ var ViewMenu = {
};
/**
* A "Configuration" set for a class of history query results. Some results
* A "Configuration" set for a class of history query results. Some results
* will require that the grouping UI be labeled differently from the standard
* so this object is provided to allow those results to configure the UI when
* they are displayed.
* @param substr
* A prefix substring of an annotation that the result's query
* A prefix substring of an annotation that the result's query
* matches.
* @param onLabel
* The label for the "Grouping On" command
@ -1468,10 +1600,10 @@ var ViewMenu = {
* @param offOncommand
* The "oncommand" attribute of the "Grouping Off" command
* @param disabled
* Whether or not grouping is disabled for results that match this
* Whether or not grouping is disabled for results that match this
* config.
*/
function GroupingConfig(substr, onLabel, onAccesskey, offLabel, offAccesskey,
function GroupingConfig(substr, onLabel, onAccesskey, offLabel, offAccesskey,
onOncommand, offOncommand, disabled) {
this.substr = substr;
this.onLabel = onLabel;
@ -1484,24 +1616,24 @@ function GroupingConfig(substr, onLabel, onAccesskey, offLabel, offAccesskey,
}
/**
* Handles Grouping within the Content View, and the commands that support it.
* Handles Grouping within the Content View, and the commands that support it.
*/
var Groupers = {
defaultGrouper: null,
annotationGroupers: [],
/**
* Initializes groupings for various view types.
* Initializes groupings for various view types.
*/
init: function G_init() {
this.defaultGrouper =
this.defaultGrouper =
new GroupingConfig(null, PlacesUtils.getString("defaultGroupOnLabel"),
PlacesUtils.getString("defaultGroupOnAccesskey"),
PlacesUtils.getString("defaultGroupOffLabel"),
PlacesUtils.getString("defaultGroupOffAccesskey"),
"Groupers.groupBySite()",
"Groupers.groupByPage()", false);
var subscriptionConfig =
var subscriptionConfig =
new GroupingConfig("livemark/", PlacesUtils.getString("livemarkGroupOnLabel"),
PlacesUtils.getString("livemarkGroupOnAccesskey"),
PlacesUtils.getString("livemarkGroupOffLabel"),
@ -1512,10 +1644,10 @@ var Groupers = {
},
/**
* Get the most appropriate GroupingConfig for the set of queries that are
* Get the most appropriate GroupingConfig for the set of queries that are
* about to be executed.
* @param queries
* An array of queries that are about to be executed.
* An array of queries that are about to be executed.
* @param handler
* Optionally specify which handler to use to filter the options.
* If null, the default handler for the queries will be used.
@ -1524,7 +1656,7 @@ var Groupers = {
if (!handler)
handler = OptionsFilter.getHandler(queries);
// If the queries will generate a bookmarks folder, there is no "grouper
// If the queries will generate a bookmarks folder, there is no "grouper
// config" since all of the grouping UI should be hidden (there is only one
// grouping mode - group by folder).
if (handler == OptionsFilter.bookmarksHandler)
@ -1542,16 +1674,16 @@ var Groupers = {
},
/**
* Updates the grouping broadcasters for the given result.
* Updates the grouping broadcasters for the given result.
* @param queries
* An array of queries that are going to be executed.
* @param options
* The options that are being used to generate the forthcoming
* The options that are being used to generate the forthcoming
* result.
* @param handler
* Optionally specify which handler to use to filter the options.
* If null, the default handler for the queries will be used.
* @param
* @param
*/
updateGroupingUI: function G_updateGroupingUI(queries, options, handler) {
var separator = document.getElementById("placesBC_grouping:separator");
@ -1560,9 +1692,9 @@ var Groupers = {
separator.removeAttribute("hidden");
groupOff.removeAttribute("hidden");
groupOn.removeAttribute("hidden");
// Walk the list of registered annotationGroupers, determining what are
// available and notifying the broadcaster
// Walk the list of registered annotationGroupers, determining what are
// available and notifying the broadcaster
var config = this._getConfig(queries, handler);
if (!config) {
// Generic Bookmarks Folder or custom container that disables grouping.
@ -1577,8 +1709,8 @@ var Groupers = {
groupOff.setAttribute("label", config.offLabel);
groupOff.setAttribute("accesskey", config.offAccesskey);
groupOff.setAttribute("oncommand", config.offOncommand);
// Update the checked state of the UI:
// Grouping UI is "enabled" if there is at least one set of grouping
// Update the checked state of the UI:
// Grouping UI is "enabled" if there is at least one set of grouping
// options.
var groupingsCountRef = { };
options.getGroupingMode(groupingsCountRef);
@ -1587,12 +1719,12 @@ var Groupers = {
},
/**
* Update the visual state of UI that controls grouping.
* Update the visual state of UI that controls grouping.
*/
_updateBroadcasters: function G__updateGroupingBroadcasters(on) {
var groupingOn = document.getElementById("placesBC_grouping:on");
var groupingOff = document.getElementById("placesBC_grouping:off");
if (on) {
if (on) {
groupingOn.setAttribute("checked", "true");
groupingOff.removeAttribute("checked");
}
@ -1603,7 +1735,7 @@ var Groupers = {
},
/**
* Shows visited pages grouped by site.
* Shows visited pages grouped by site.
*/
groupBySite: function G_groupBySite() {
var query = asQuery(PlacesOrganizer._content.getResult().root);
@ -1620,7 +1752,7 @@ var Groupers = {
},
/**
* Shows visited pages without grouping.
* Shows visited pages without grouping.
*/
groupByPage: function G_groupByPage() {
var query = asQuery(PlacesOrganizer._content.getResult().root);
@ -1636,7 +1768,7 @@ var Groupers = {
},
/**
* Shows all subscribed feeds (Live Bookmarks) grouped under their parent
* Shows all subscribed feeds (Live Bookmarks) grouped under their parent
* feed.
*/
groupByFeed: function G_groupByFeed() {
@ -1668,4 +1800,3 @@ var Groupers = {
OptionsFilter.update(content.getResult());
}
};

View File

@ -45,6 +45,8 @@
<?xml-stylesheet href="chrome://global/skin/"?>
<?xml-stylesheet href="chrome://browser/skin/places/places.css"?>
<?xul-overlay href="chrome://browser/content/places/editBookmarkOverlay.xul"?>
#ifdef XP_MACOSX
<?xul-overlay href="chrome://browser/content/macBrowserOverlay.xul"?>
#else
@ -64,6 +66,7 @@
title="&places.organizer.title;"
windowtype="Places:Organizer"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml"
onload="PlacesOrganizer.init();"
onunload="PlacesOrganizer.destroy();"
width="700" height="500" screenX="10" screenY="10"
@ -71,6 +74,8 @@
<script type="application/x-javascript"
src="chrome://browser/content/places/places.js"/>
<script type="application/javascript"
src="chrome://browser/content/utilityOverlay.js"/>
#ifdef XP_MACOSX
#include ../../../base/content/browserMountPoints.inc
@ -240,22 +245,13 @@
accesskey="&selectAllCmd.accesskey;"/>
<menuseparator id="orgMoveSeparator"/>
<menuitem id="orgDelete"
command="cmd_delete"
label="&deleteCmd.label;"
key="key_delete"
accesskey="&deleteCmd.accesskey;"/>
<menuitem id="orgRename"
command="placesCmd_rename"
label="&cmd.rename.label;"
accesskey="&cmd.rename.accesskey;"/>
<menuitem id="orgProperties"
command="placesCmd_show:info"
label="&cmd.properties.label;"
key="placesKey_show:info"
accesskey="&cmd.properties.accesskey;"/>
<menuseparator id="orgCloseSeparator"/>
<menuitem id="orgClose"
@ -270,6 +266,7 @@
label="&views.label;"
accesskey="&view.accesskey;">
<menupopup id="viewMenuPopup">
<!--
<menuitem id="viewDetails"
type="radio"
#ifdef MACOSX
@ -282,18 +279,19 @@
<menuseparator id="addonsSeparator"/>
<!--
<menuitem id="viewAddons"
command=""
label="&view.addons.label;"
accesskey="&view.addons.label;"/>
<menuseparator id="sortingSeparator"/>
-->
-->
<menu id="viewColumns"
label="&view.columns.label;" accesskey="&view.columns.accesskey;">
<menupopup onpopupshowing="ViewMenu.fillWithColumns(event, null, null, 'checkbox', null);"
oncommand="ViewMenu.showHideColumn(event.target); event.stopPropagation();"/>
</menu>
<menu id="viewSort" label="&view.sort.label;"
accesskey="&view.sort.accesskey;">
<menupopup onpopupshowing="ViewMenu.populateSortMenu(event);"
@ -310,14 +308,14 @@
oncommand="ViewMenu.setSortColumn(null, 'descending'); event.stopPropagation();"/>
</menupopup>
</menu>
<!--
<!--
<menuseparator id="groupingSeparator" observes="placesBC_grouping:separator"/>
<menuitem id="viewGroupNone" type="radio" name="group"
observes="placesBC_grouping:off"/>
<menuitem id="viewGroupGroup" type="radio" name="group"
observes="placesBC_grouping:on"/>
-->
-->
</menupopup>
</toolbarbutton>
@ -408,41 +406,75 @@
command="OrganizerCommand_search:moreCriteria"/>
</hbox>
</hbox>
#include advancedSearch.inc
<tree id="placeContent" class="placesTree" context="placesContext"
flex="1" type="places"
ondblclick="this.controller.openSelectedNodeWithEvent(event);"
onclick="PlacesOrganizer.onTreeClick(event);">
<treecols id="placeContentColumns">
<treecol label="&col.name.label;" id="title" flex="5" primary="true"
persist="width hidden ordinal sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol label="&col.tags.label;" id="tags" flex="2"
persist="width hidden ordinal sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol label="&col.url.label;" id="url" flex="5"
persist="width hidden ordinal sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol label="&col.lastvisit.label;" id="date" flex="1" hidden="true"
persist="width hidden ordinal sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol label="&col.visitcount.label;" id="visitCount" flex="1" hidden="true"
persist="width hidden ordinal sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol label="&col.keyword.label;" id="keyword" flex="1" hidden="true"
persist="width hidden ordinal sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol label="&col.description.label;" id="description" flex="1" hidden="true"
persist="width hidden ordinal sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol label="&col.dateadded.label;" id="dateAdded" flex="1" hidden="true"
persist="width hidden ordinal sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol label="&col.lastmodified.label;" id="lastModified" flex="1" hidden="true"
persist="width hidden ordinal sortActive sortDirection"/>
</treecols>
<treechildren id="placeContentChildren" view="placeContent" flex="1"/>
</tree>
#include advancedSearch.inc
<vbox flex="1">
<tree id="placeContent" class="placesTree" context="placesContext"
flex="1" type="places"
ondblclick="this.controller.openSelectedNodeWithEvent(event);"
onclick="PlacesOrganizer.onTreeClick(event);"
onselect="PlacesOrganizer.onContentTreeSelect();">
<treecols id="placeContentColumns">
<treecol label="&col.name.label;" id="title" flex="5" primary="true"
persist="width hidden ordinal sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol label="&col.tags.label;" id="tags" flex="2"
persist="width hidden ordinal sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol label="&col.url.label;" id="url" flex="5"
persist="width hidden ordinal sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol label="&col.lastvisit.label;" id="date" flex="1" hidden="true"
persist="width hidden ordinal sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol label="&col.visitcount.label;" id="visitCount" flex="1" hidden="true"
persist="width hidden ordinal sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol label="&col.keyword.label;" id="keyword" flex="1" hidden="true"
persist="width hidden ordinal sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol label="&col.description.label;" id="description" flex="1" hidden="true"
persist="width hidden ordinal sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol label="&col.dateadded.label;" id="dateAdded" flex="1" hidden="true"
persist="width hidden ordinal sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol label="&col.lastmodified.label;" id="lastModified" flex="1" hidden="true"
persist="width hidden ordinal sortActive sortDirection"/>
</treecols>
<treechildren id="placeContentChildren" view="placeContent" flex="1"/>
</tree>
<splitter id="contentSplitter" collapse="after" persist="state">
<grippy/>
</splitter>
<hbox persist="height" id="infoPane">
<hbox id="previewBox" style="min-width: 60px; -moz-border-end: 1px solid GrayText;">
<html:canvas id="itemThumbnail" notavailabletext="&detailsPane.noPreviewAvailable.label;"/>
</hbox>
<deck flex="1" id="detailsDeck">
<vbox align="center">
<spacer flex="3"/>
<label id="itemsCountText"/>
<spacer flex="1"/>
<description id="selectItemDescription">
&detailsPane.selectAnItemText.description;
</description>
<spacer flex="3"/>
</vbox>
<scrollbox id="infoScrollbox" minimal="true" orient="vertical" flex="1" style='overflow: auto;'>
<vbox id="editBookmarkPanelContent"/>
<hbox>
<button type="image" id="infoScrollboxExpander"
lesslabel="&detailsPane.less.label;"
morelabel="&detailsPane.more.label;"
label="&detailsPane.more.label;"
oncommand="PlacesOrganizer.toggleAdditionalInfoFields();"
observes="paneElementsBroadcaster"/>
<spacer flex="1"/>
</hbox>
</scrollbox>
</deck>
</hbox>
</vbox>
<vbox id="debugPanel" hidden="true">
<grid>
<columns>

View File

@ -52,6 +52,7 @@ const LOAD_IN_SIDEBAR_ANNO = "bookmarkProperties/loadInSidebar";
const DESCRIPTION_ANNO = "bookmarkProperties/description";
const POST_DATA_ANNO = "URIProperties/POSTData";
const LMANNO_FEEDURI = "livemark/feedURI";
const LMANNO_SITEURI = "livemark/siteURI";
#ifdef XP_MACOSX
// On Mac OSX, the transferable system converts "\r\n" to "\n\n", where we
@ -408,7 +409,7 @@ var PlacesUtils = {
*/
nodeIsLivemarkContainer: function PU_nodeIsLivemarkContainer(aNode) {
return this.nodeIsFolder(aNode) &&
this.annotations.itemHasAnnotation(aNode.itemId, LMANNO_FEEDURI);
this.livemarks.isLivemark(aNode.itemId);
},
/**
@ -457,7 +458,7 @@ var PlacesUtils = {
},
/**
* String-wraps a NavHistoryResultNode according to the rules of the specified
* String-wraps a result node according to the rules of the specified
* content type.
* @param aNode
* The Result node to wrap (serialize)

View File

@ -1,5 +1,7 @@
<!ENTITY editBookmarkOverlay.name.label "Name:">
<!ENTITY editBookmarkOverlay.location.label "Location:">
<!ENTITY editBookmarkOverlay.feedLocation.label "Feed Location:">
<!ENTITY editBookmarkOverlay.siteLocation.label "Site Location:">
<!ENTITY editBookmarkOverlay.liveTitlesSeparator.label "Live Titles">
<!ENTITY editBookmarkOverlay.folder.label "Folder:">
<!ENTITY editBookmarkOverlay.allBookmarksFolderItem.label "All Bookmarks">
@ -9,4 +11,6 @@
<!ENTITY editBookmarkOverlay.expanderUp.tooltip "Hide">
<!ENTITY editBookmarkOverlay.tags.label "Tags:">
<!ENTITY editBookmarkOverlay.description.label "Description:">
<!ENTITY editBookmarkOverlay.keyword.label "Keyword:">
<!ENTITY editBookmarkOverlay.tagsExpanderDown.tooltip "Show all tags">
<!ENTITY editBookmarkOverlay.loadInSidebar.label "Load this bookmark in the sidebar">

View File

@ -191,3 +191,12 @@
<!ENTITY forwardCmd.label "Forward">
<!ENTITY forwardCmd.accesskey "F">
<!ENTITY forwardButton.tooltip "Go forward">
<!ENTITY detailsPane.more.label
"More">
<!ENTITY detailsPane.less.label
"Less">
<!ENTITY detailsPane.noPreviewAvailable.label
"Preview">
<!ENTITY detailsPane.selectAnItemText.description
"Select an item to view and edit its properties">

View File

@ -93,3 +93,7 @@ EnterExport=Export Bookmarks File
saveSearch.title=Save Search
saveSearch.inputLabel=Name:
saveSearch.defaultText=New Query
detailsPane.noItems=No items
detailsPane.oneItem=One item
detailsPane.multipleItems=%S items

View File

@ -49,6 +49,8 @@ classic.jar:
skin/classic/browser/places/editBookmarkOverlay.css (places/editBookmarkOverlay.css)
skin/classic/browser/places/starPage.png (places/starPage.png)
skin/classic/browser/places/pageStarred.png (places/pageStarred.png)
skin/classic/browser/places/twisty-open.gif (places/twisty-open.gif)
skin/classic/browser/places/twisty-closed.gif (places/twisty-closed.gif)
skin/classic/browser/places/tag.png (places/tag.png)
skin/classic/browser/places/organizer-toolbar.png (bookmarks/Bookmarks-toolbar.png)
skin/classic/browser/places/expander-closed-active.png (bookmarks/expander-closed-active.png)

View File

@ -112,17 +112,28 @@
*/
-moz-appearance: textfield;
cursor: text;
margin: 2px 4px;
border: 2px solid;
-moz-border-top-colors: ThreeDShadow ThreeDDarkShadow;
-moz-border-right-colors: ThreeDHighlight ThreeDLightShadow;
-moz-border-bottom-colors: ThreeDHighlight ThreeDLightShadow;
-moz-border-left-colors: ThreeDShadow ThreeDDarkShadow;
padding: 2px 2px 3px 4px;
margin: 4px 4px;
border: 3px solid;
-moz-border-top-colors: transparent #888888 #000000;
-moz-border-right-colors: transparent #FFFFFF #000000;
-moz-border-bottom-colors: transparent #FFFFFF #000000;
-moz-border-left-colors: transparent #888888 #000000;
-moz-border-radius-topright: 2px;
-moz-border-radius-bottomleft: 2px;
padding: 0;
background-color: -moz-Field;
color: -moz-FieldText;
}
#editBMPanel_namePicker[droppable="false"][disabled="true"] > .menulist-editable-box {
cursor: default;
-moz-border-top-colors: transparent ThreeDShadow -moz-Dialog;
-moz-border-right-colors: transparent ThreeDShadow -moz-Dialog;
-moz-border-bottom-colors: transparent ThreeDShadow -moz-Dialog;
-moz-border-left-colors: transparent ThreeDShadow -moz-Dialog;
color: GrayText;
}
#editBMPanel_namePicker[droppable="false"] > .menulist-editable-box > html|*.textbox-input {
margin: 0px !important;
border: none !important;

View File

@ -272,3 +272,24 @@ treechildren::-moz-tree-twisty(title, separator) {
min-width:0em;
}
/**
* info pane
*/
/* More/Less button */
#infoScrollboxExpander {
list-style-image: url("chrome://browser/skin/places/twisty-open.gif");
-moz-appearance: none;
margin: 0;
padding: 0;
max-width: 0;
}
#infoScrollbox[minimal="true"] #infoScrollboxExpander {
list-style-image: url("chrome://browser/skin/places/twisty-closed.gif");
}
#itemsCountText,
#selectItemDescription {
color: GrayText;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 B

View File

@ -358,7 +358,7 @@
<binding id="menulist-editable" extends="chrome://global/content/bindings/menulist.xml#menulist">
<content sizetopopup="pref">
<xul:hbox class="menulist-editable-box textbox-input-box" xbl:inherits="context" flex="1">
<xul:hbox class="menulist-editable-box textbox-input-box" xbl:inherits="context,disabled,readonly" flex="1">
<html:input class="menulist-editable-input" flex="1" anonid="input" allowevents="true"
xbl:inherits="value=label,value,disabled,tabindex,readonly"/>
</xul:hbox>
@ -493,6 +493,11 @@
]]></getter>
</property>
<property name="readOnly" onset="this.inputField.readOnly = val;
if (val) this.setAttribute('readonly', 'true');
else this.removeAttribute('readonly'); return val;"
onget="return this.inputField.readOnly;"/>
<method name="select">
<body>
this.inputField.select();