Library switcher in advanced search window

When opening the advanced search window, the current library is
selected, and a different library can be selected to change the search
scope. If a library is read-only, the saved search button is disabled.
For saved searches, the appropriate library is selected and the
drop-down is disabled.

Also:

- Close the advanced search window after a search is saved
- The default name for saved searches ("Untitled 2", etc.) was based on
  collections rather than searches
- Once an initial search has been performed, the drop-downs and
  checkboxes now update the results
- More consistent spacing in advanced search window
- (dev) Zotero.DB.getNextName() now takes a libraryID as its first
  parameter instead of always using My Library; the old parameters are
  deprecated but still work
This commit is contained in:
Dan Stillman 2014-05-15 03:19:49 -04:00
parent f8798fe996
commit 9f91d240b0
10 changed files with 198 additions and 78 deletions

View file

@ -43,6 +43,7 @@ var ZoteroAdvancedSearch = new function() {
var sbc = document.getElementById('zotero-search-box-container');
Zotero.setFontSize(sbc);
_searchBox.onLibraryChange = this.onLibraryChange;
var io = window.arguments[0];
_searchBox.search = io.dataIn.search;
}
@ -50,24 +51,33 @@ var ZoteroAdvancedSearch = new function() {
function search() {
_searchBox.updateSearch();
_searchBox.active = true;
// A minimal implementation of Zotero.CollectionTreeView
var itemGroup = {
isSearchMode: function() { return true; },
getItems: function () {
//var search = _searchBox.search.clone();
var search = _searchBox.search.clone();
var s2 = new Zotero.Search();
s2.setScope(_searchBox.search);
// FIXME: Hack to exclude group libraries for now
var groups = Zotero.Groups.getAll();
for each(var group in groups) {
s2.addCondition('libraryID', 'isNot', group.libraryID);
// Hack to create a condition for the search's library --
// this logic should really go in the search itself instead of here
// and in collectionTreeView.js
var conditions = search.getSearchConditions();
if (!conditions.some(function (condition) condition.condition == 'libraryID')) {
let libraryID = _searchBox.search.libraryID;
// TEMP: libraryIDInt
if (libraryID) {
search.addCondition('libraryID', 'is', libraryID);
}
else {
let groups = Zotero.Groups.getAll();
for (let i=0; i<groups.length; i++) {
search.addCondition('libraryID', 'isNot', groups[i].libraryID);
}
}
}
var ids = s2.search();
return Zotero.Items.get(ids);
return Zotero.Items.get(search.search());
},
isLibrary: function () { return false; },
isCollection: function () { return false; },
@ -92,8 +102,11 @@ var ZoteroAdvancedSearch = new function() {
document.getElementById('zotero-items-tree').view = null;
var s = new Zotero.Search();
// Don't clear the selected library
s.libraryID = _searchBox.search.libraryID;
s.addCondition('title', 'contains', '');
_searchBox.search = s;
_searchBox.active = false;
}
@ -103,8 +116,12 @@ var ZoteroAdvancedSearch = new function() {
var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService);
var untitled = Zotero.DB.getNextName('collections', 'collectionName',
Zotero.getString('pane.collections.untitled'));
var untitled = Zotero.DB.getNextName(
_searchBox.search.libraryID,
'savedSearches',
'savedSearchName',
Zotero.getString('pane.collections.untitled')
);
var name = { value: untitled };
var result = promptService.prompt(window,
@ -124,6 +141,13 @@ var ZoteroAdvancedSearch = new function() {
var s = _searchBox.search.clone();
s.name = name.value;
s.save();
window.close();
}
this.onLibraryChange = function (libraryID) {
document.getElementById('zotero-search-save').disabled = !Zotero.Libraries.isEditable(libraryID);
}

View file

@ -25,35 +25,33 @@
<script src="advancedSearch.js"/>
<vbox id="zotero-search-box-container" flex="1">
<hbox>
<zoterosearch id="zotero-search-box" oncommand="ZoteroAdvancedSearch.search()" flex="1"/>
</hbox>
<hbox id="zotero-search-buttons">
<button label="&zotero.search.search;" default="true" oncommand="ZoteroAdvancedSearch.search()"/>
<button label="&zotero.search.clear;" oncommand="ZoteroAdvancedSearch.clear()"/>
<button label="&zotero.search.saveSearch;" oncommand="ZoteroAdvancedSearch.save()"/>
</hbox>
<tree id="zotero-items-tree" flex="1" hidecolumnpicker="true" seltype="multiple"
ondblclick="ZoteroAdvancedSearch.onDblClick(event, this)"
ondragstart="if (event.target.localName == 'treechildren') { ZoteroAdvancedSearch.itemsView.onDragStart(event); }">
<treecols>
<treecol
id="zotero-items-column-title" primary="true"
label="&zotero.items.title_column;"
flex="4" persist="width ordinal hidden sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol
id="zotero-items-column-firstCreator"
label="&zotero.items.creator_column;"
flex="1" persist="width ordinal hidden sortActive sortDirection"/>
<splitter class="tree-splitter"/>
</treecols>
<treechildren alternatingbackground="true"/>
</tree>
<vbox id="zotero-search-box-controls">
<zoterosearch id="zotero-search-box" oncommand="if (this.active) { ZoteroAdvancedSearch.search(); }" flex="1"/>
<hbox id="zotero-search-buttons">
<button label="&zotero.search.search;" default="true" oncommand="ZoteroAdvancedSearch.search()"/>
<button label="&zotero.search.clear;" oncommand="ZoteroAdvancedSearch.clear()"/>
<button id="zotero-search-save" label="&zotero.search.saveSearch;" oncommand="ZoteroAdvancedSearch.save()"/>
</hbox>
</vbox>
<tree id="zotero-items-tree" flex="1" hidecolumnpicker="true" seltype="multiple"
ondblclick="ZoteroAdvancedSearch.onDblClick(event, this)"
ondragstart="if (event.target.localName == 'treechildren') { ZoteroAdvancedSearch.itemsView.onDragStart(event); }">
<treecols>
<treecol
id="zotero-items-column-title" primary="true"
label="&zotero.items.title_column;"
flex="4" persist="width ordinal hidden sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol
id="zotero-items-column-firstCreator"
label="&zotero.items.creator_column;"
flex="1" persist="width ordinal hidden sortActive sortDirection"/>
<splitter class="tree-splitter"/>
</treecols>
<treechildren alternatingbackground="true"/>
</tree>
</vbox>
<keyset>

View file

@ -42,6 +42,8 @@
<![CDATA[
this.searchRef = val;
this.buildLibraryMenu();
var conditionsBox = this.id('conditions');
while(conditionsBox.hasChildNodes())
conditionsBox.removeChild(conditionsBox.firstChild);
@ -74,6 +76,49 @@
]]>
</setter>
</property>
<method name="buildLibraryMenu">
<body><![CDATA[
var menulist = this.id('libraryMenu');
var menupopup = menulist.firstChild;
while (menupopup.hasChildNodes()) {
menupopup.removeChild(menupopup.firstChild);
}
var libraryID = this.searchRef.libraryID;
var libraryIndex = 0;
// Add My Library
var menuitem = document.createElement('menuitem');
menuitem.setAttribute('label', Zotero.getString('pane.collections.library'));
menuitem.setAttribute('libraryID', 0);
menupopup.appendChild(menuitem);
// Add groups
var groups = Zotero.Groups.getAll();
for (let i=0; i<groups.length; i++) {
let group = groups[i];
let menuitem = document.createElement('menuitem');
menuitem.setAttribute('label', group.name);
menuitem.setAttribute('libraryID', group.libraryID);
if (group.libraryID == libraryID) {
libraryIndex = i + 1;
}
menupopup.appendChild(menuitem);
}
menulist.appendChild(menupopup);
menulist.selectedIndex = libraryIndex;
if (this.searchRef.id) {
this.id('libraryMenu').disabled = true;
}
this.updateLibrary();
]]></body>
</method>
<method name="addCondition">
<parameter name="ref"/>
<body>
@ -100,6 +145,7 @@
]]>
</body>
</method>
<method name="removeCondition">
<parameter name="id"/>
<body>
@ -121,6 +167,21 @@
]]>
</body>
</method>
<method name="updateLibrary">
<body><![CDATA[
var menu = this.id('libraryMenu');
var libraryID = parseInt(menu.selectedItem.getAttribute('libraryID'));
if (this.onLibraryChange) {
this.onLibraryChange(libraryID);
}
// TODO: libraryIDInt
this.searchRef.libraryID = libraryID ? libraryID : null;
]]></body>
</method>
<method name="updateJoinMode">
<body>
<![CDATA[
@ -132,6 +193,7 @@
]]>
</body>
</method>
<method name="updateCheckbox">
<parameter name="condition"/>
<body>
@ -151,12 +213,12 @@
]]>
</body>
</method>
<!-- Calls updateSearch() on all search conditions -->
<method name="updateSearch">
<body>
<![CDATA[
var conditionsBox = this.id('conditions');
if (conditionsBox.hasChildNodes()) {
for(var i = 0, len=conditionsBox.childNodes.length; i < len; i++) {
conditionsBox.childNodes[i].updateSearch();
@ -165,6 +227,7 @@
]]>
</body>
</method>
<method name="save">
<body>
<![CDATA[
@ -181,6 +244,8 @@
switch (event.keyCode) {
case event.DOM_VK_RETURN:
case event.DOM_VK_ENTER:
this.active = true;
if (event.shiftKey) {
this.addCondition();
}
@ -193,7 +258,6 @@
</body>
</method>
<method name="id">
<parameter name="id"/>
<body>
@ -207,10 +271,16 @@
<content>
<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
id="search-box" flex="1" onkeypress="document.getBindingParent(this).handleKeyPress(event)">
<hbox align="center">
<label value="&zotero.search.searchInLibrary;" control="libraryMenu"/>
<menulist id="libraryMenu" oncommand="document.getBindingParent(this).updateLibrary();">
<menupopup/>
</menulist>
</hbox>
<groupbox xbl:inherits="flex">
<caption align="center">
<label value="&zotero.search.joinMode.prefix;"/>
<menulist id="joinModeMenu" oncommand="document.getBindingParent(this).updateJoinMode(); event.stopPropagation()">
<menulist id="joinModeMenu" oncommand="document.getBindingParent(this).updateJoinMode();">
<menupopup>
<menuitem label="&zotero.search.joinMode.any;" value="any"/>
<menuitem label="&zotero.search.joinMode.all;" value="all" selected="true"/>
@ -221,10 +291,12 @@
<vbox id="conditions"/>
</groupbox>
<hbox>
<checkbox id="recursiveCheckbox" label="&zotero.search.recursive.label;" oncommand="document.getBindingParent(this).updateCheckbox('recursive'); event.stopPropagation()"/>
<checkbox id="noChildrenCheckbox" label="&zotero.search.noChildren;" oncommand="document.getBindingParent(this).updateCheckbox('noChildren'); event.stopPropagation()"/>
<checkbox id="recursiveCheckbox" label="&zotero.search.recursive.label;" oncommand="document.getBindingParent(this).updateCheckbox('recursive');"/>
<checkbox id="noChildrenCheckbox" label="&zotero.search.noChildren;" oncommand="document.getBindingParent(this).updateCheckbox('noChildren');"/>
</hbox>
<hbox>
<checkbox id="includeParentsAndChildrenCheckbox" label="&zotero.search.includeParentsAndChildren;" oncommand="document.getBindingParent(this).updateCheckbox('includeParentsAndChildren');"/>
</hbox>
<checkbox id="includeParentsAndChildrenCheckbox" label="&zotero.search.includeParentsAndChildren;" oncommand="document.getBindingParent(this).updateCheckbox('includeParentsAndChildren'); event.stopPropagation()"/>
</vbox>
</content>
</binding>

View file

@ -703,13 +703,28 @@ Zotero.DBConnection.prototype.getNextID = function (table, column) {
*
* If _name_ alone is available, returns that
**/
Zotero.DBConnection.prototype.getNextName = function (table, field, name)
Zotero.DBConnection.prototype.getNextName = function (libraryID, table, field, name)
{
if (typeof name == 'undefined') {
Zotero.debug("WARNING: The parameters of Zotero.DB.getNextName() have changed -- update your code", 2);
[libraryID, table, field, name] = [null, libraryID, table, field];
}
var sql = "SELECT TRIM(SUBSTR(" + field + ", " + (name.length + 1) + ")) "
+ "FROM " + table + " "
+ "WHERE " + field + " REGEXP '^" + name + "( [0-9]+)?$' "
+ "ORDER BY " + field;
var suffixes = this.columnQuery(sql);
+ "WHERE " + field + " REGEXP '^" + name + "( [0-9]+)?$' ";
if (!libraryID) {
// DEBUG: Shouldn't this be replaced automatically with "=?"?
sql += " AND libraryID IS NULL";
var params = undefined
}
else {
sql += " AND libraryID=?";
var params = [libraryID];
}
sql += " ORDER BY " + field;
// TEMP: libraryIDInt
var suffixes = this.columnQuery(sql, params);
// If none found or first one has a suffix, use default name
if (!suffixes || suffixes[0]) {
return name;

View file

@ -107,7 +107,7 @@ Zotero.Search.prototype._set = function (field, val) {
}
if (this._loaded) {
throw ("Cannot set " + field + " after object is already loaded in Zotero.Search._set()");
throw new Error("Cannot set " + field + " after object is already loaded");
}
//this._checkValue(field, val);
this['_' + field] = val;
@ -379,6 +379,7 @@ Zotero.Search.prototype.save = function(fixGaps) {
Zotero.Search.prototype.clone = function() {
var s = new Zotero.Search();
s.libraryID = this.libraryID;
var conditions = this.getSearchConditions();

View file

@ -977,6 +977,7 @@ var ZoteroPane = new function()
}
var s = new Zotero.Search();
s.libraryID = this.getSelectedLibraryID();
s.addCondition('title', 'contains', '');
var io = {dataIn: {search: s}, dataOut: null};
window.openDialog('chrome://zotero/content/advancedSearch.xul', '', 'chrome,dialog=no,centerscreen', io);

View file

@ -1,5 +1,7 @@
<!ENTITY zotero.search.name "Name:">
<!ENTITY zotero.search.searchInLibrary "Search in library:">
<!ENTITY zotero.search.joinMode.prefix "Match">
<!ENTITY zotero.search.joinMode.any "any">
<!ENTITY zotero.search.joinMode.all "all">

View file

@ -3,8 +3,27 @@
width: 60em;
}
#search-box > hbox {
margin-left: 6px;
}
groupbox {
margin-top: 0;
padding-top: 0;
}
caption {
font: inherit;
padding-left: 0 !important;
}
label:first-child, checkbox:first-child {
margin-left: 0 !important;
padding-left: 0 !important;
}
checkbox {
margin-right: .5em;
}
#search-condition menulist[id="operatorsmenu"]
@ -48,24 +67,3 @@ caption {
{
min-width: 3em;
}
#zotero-advanced-search-dialog
{
padding: 8px 8px 14px;
height: 400px;
}
#zotero-advanced-search-dialog #zotero-search-buttons
{
margin: 3px 0;
}
#zotero-advanced-search-dialog checkbox
{
margin-right: .5em;
}
#zotero-advanced-search-dialog #zotero-items-tree
{
min-height: 170px;
}

View file

@ -214,11 +214,6 @@
color: inherit;
}
#zotero-advanced-search-dialog #zotero-items-tree
{
min-height: 250px;
}
#zotero-items-pane
{
min-width: 290px;

View file

@ -340,4 +340,18 @@ label.zotero-text-link {
font-weight: bold;
color: red;
text-align: center;
}
#zotero-advanced-search-dialog #zotero-search-box-controls {
padding: 3px;
}
#zotero-advanced-search-dialog #zotero-items-tree
{
min-height: 250px;
}
#zotero-advanced-search-dialog #zotero-search-buttons
{
margin: 3px 0;
}