Fixes #616, Tags don't appear in tag selector for child items
Also, child items in search results now always display in context, with the non-matching rows in gray. May later implement Elena's idea to show an ellipsis to toggle non-matching items. New method Search.setScope(searchObj) to pass one search as the scope for another -- used to make quicksearch and tag selector properly search within the current view. Note: These changes need testing -- I may not have gotten all the various conditions (view mode, quicksearch, tag selector, advanced search, drag-and-drop) exactly right.
This commit is contained in:
parent
c6f4a0d023
commit
0dbf98ccf4
|
@ -27,6 +27,7 @@ var ZoteroAdvancedSearch = new function() {
|
|||
|
||||
// A minimal implementation of Zotero.CollectionTreeView
|
||||
var itemGroup = {
|
||||
isSearchMode: function() { return true; },
|
||||
getChildItems: function () {
|
||||
var ids = _searchBox.search.search();
|
||||
return Zotero.Items.get(ids);
|
||||
|
|
|
@ -834,51 +834,42 @@ Zotero.ItemGroup.prototype.getChildItems = function()
|
|||
* This accounts for the collection, saved search, quicksearch, tags, etc.
|
||||
*/
|
||||
Zotero.ItemGroup.prototype.getSearchObject = function() {
|
||||
// Create/load the inner search
|
||||
var s = new Zotero.Search();
|
||||
|
||||
if (this.searchText){
|
||||
if (this.isCollection())
|
||||
{
|
||||
s.addCondition('collectionID', 'is', this.ref.getID());
|
||||
if (Zotero.Prefs.get('recursiveCollections')) {
|
||||
s.addCondition('recursive', 'true');
|
||||
}
|
||||
}
|
||||
else if (this.isSearch())
|
||||
{
|
||||
s.addCondition('savedSearchID', 'is', this.ref['id']);
|
||||
}
|
||||
s.addCondition('quicksearch', 'contains', this.searchText);
|
||||
if (this.isLibrary()) {
|
||||
s.addCondition('noChildren', true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.isLibrary()){
|
||||
s.addCondition('noChildren', 'true');
|
||||
}
|
||||
else if (this.isCollection()){
|
||||
s.addCondition('noChildren', 'true');
|
||||
s.addCondition('collectionID', 'is', this.ref.getID());
|
||||
if (Zotero.Prefs.get('recursiveCollections')) {
|
||||
s.addCondition('recursive', 'true');
|
||||
}
|
||||
}
|
||||
else if (this.isSearch()){
|
||||
s.load(this.ref['id']);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
else if (this.isCollection()) {
|
||||
s.addCondition('noChildren', true);
|
||||
s.addCondition('collectionID', 'is', this.ref.getID());
|
||||
if (Zotero.Prefs.get('recursiveCollections')) {
|
||||
s.addCondition('recursive', 'true');
|
||||
}
|
||||
}
|
||||
else if (this.isSearch()){
|
||||
s.load(this.ref['id']);
|
||||
}
|
||||
else {
|
||||
throw ('Invalid search mode in Zotero.ItemGroup.getSearchObject()');
|
||||
}
|
||||
|
||||
// Create the outer (filter) search
|
||||
var s2 = new Zotero.Search();
|
||||
s2.setScope(s);
|
||||
|
||||
if (this.searchText) {
|
||||
s2.addCondition('quicksearch', 'contains', this.searchText);
|
||||
}
|
||||
|
||||
if (this.tags){
|
||||
for (var tag in this.tags){
|
||||
if (this.tags[tag]){
|
||||
s.addCondition('tag', 'is', tag);
|
||||
s2.addCondition('tag', 'is', tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
return s2;
|
||||
}
|
||||
|
||||
|
||||
|
@ -899,4 +890,17 @@ Zotero.ItemGroup.prototype.setSearch = function(searchText)
|
|||
Zotero.ItemGroup.prototype.setTags = function(tags)
|
||||
{
|
||||
this.tags = tags;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns TRUE if using quicksearch or tag filter
|
||||
*/
|
||||
Zotero.ItemGroup.prototype.isSearchMode = function() {
|
||||
if (this.tags) {
|
||||
for (var i in this.tags) {
|
||||
var hasTags = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return this.searchText != '' || hasTags;
|
||||
}
|
|
@ -3671,13 +3671,25 @@ Zotero.Tags = new function(){
|
|||
var tags = Zotero.DB.query(sql);
|
||||
}
|
||||
else {
|
||||
var sql = "CREATE TEMPORARY TABLE tmpSearchResults AS " + search.getSQL();
|
||||
Zotero.DB.query(sql, search.getSQLParams());
|
||||
sql = "CREATE INDEX tmpSearchResults_itemID ON tmpSearchResults(itemID)";
|
||||
Zotero.DB.query(sql);
|
||||
|
||||
var sql = "SELECT DISTINCT tagID, tag, tagType FROM itemTags "
|
||||
+ "NATURAL JOIN tags WHERE itemID IN (" + search.getSQL() + ") ";
|
||||
+ "NATURAL JOIN tags WHERE ("
|
||||
+ "itemID IN (SELECT itemID FROM tmpSearchResults) OR "
|
||||
+ "itemID IN (SELECT itemID FROM itemNotes WHERE sourceItemID IN (SELECT itemID FROM tmpSearchResults)) OR "
|
||||
+ "itemID IN (SELECT itemID FROM itemAttachments WHERE sourceItemID IN (SELECT itemID FROM tmpSearchResults))"
|
||||
+ ") ";
|
||||
if (types) {
|
||||
sql += "AND tagType IN (" + types.join() + ") ";
|
||||
}
|
||||
sql += "ORDER BY tag COLLATE NOCASE";
|
||||
var tags = Zotero.DB.query(sql, search.getSQLParams());
|
||||
var tags = Zotero.DB.query(sql);
|
||||
|
||||
sql = "DROP TABLE tmpSearchResults";
|
||||
Zotero.DB.query(sql);
|
||||
}
|
||||
|
||||
var indexed = {};
|
||||
|
|
|
@ -117,6 +117,7 @@ Zotero.ItemTreeView.prototype.setTree = function(treebox)
|
|||
}, false);
|
||||
|
||||
obj.sort();
|
||||
obj.expandMatchParents();
|
||||
|
||||
//Zotero.debug('Running callbacks in itemTreeView.setTree()', 4);
|
||||
obj._runCallbacks();
|
||||
|
@ -143,8 +144,12 @@ Zotero.ItemTreeView.prototype.setTree = function(treebox)
|
|||
*/
|
||||
Zotero.ItemTreeView.prototype.refresh = function()
|
||||
{
|
||||
this._searchMode = this._itemGroup.isSearchMode();
|
||||
|
||||
var oldRows = this.rowCount;
|
||||
this._dataItems = [];
|
||||
this._searchItemIDs = {}; // items matching the search
|
||||
this._searchParentIDs = {};
|
||||
this.rowCount = 0;
|
||||
|
||||
var cacheFields = ['title', 'date'];
|
||||
|
@ -156,7 +161,8 @@ Zotero.ItemTreeView.prototype.refresh = function()
|
|||
catch (e) {
|
||||
return;
|
||||
}
|
||||
for each(var field in visibleFields) {
|
||||
for (var i=0; i<visibleFields.length; i++) {
|
||||
var field = visibleFields[i];
|
||||
if (field == 'year') {
|
||||
field = 'date';
|
||||
}
|
||||
|
@ -167,10 +173,33 @@ Zotero.ItemTreeView.prototype.refresh = function()
|
|||
Zotero.Items.cacheFields(cacheFields);
|
||||
|
||||
var newRows = this._itemGroup.getChildItems();
|
||||
var added = 0;
|
||||
|
||||
for (var i=0, len=newRows.length; i < len; i++) {
|
||||
if (newRows[i] &&
|
||||
(!this._sourcesOnly || (!newRows[i].isAttachment() && !newRows[i].isNote()))) {
|
||||
this._showItem(new Zotero.ItemTreeView.TreeRow(newRows[i], 0, false), i+1); //item ref, before row
|
||||
// Only add regular items if sourcesOnly is set
|
||||
if (this._sourcesOnly && !newRows[i].isRegularItem()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Don't add child items directly
|
||||
var sourceItemID = newRows[i].getSource();
|
||||
if (sourceItemID) {
|
||||
this._searchParentIDs[sourceItemID] = true;
|
||||
}
|
||||
// Add top-level items
|
||||
else {
|
||||
this._showItem(new Zotero.ItemTreeView.TreeRow(newRows[i], 0, false), added + 1); //item ref, before row
|
||||
added++;
|
||||
}
|
||||
this._searchItemIDs[newRows[i].getID()] = true;
|
||||
}
|
||||
|
||||
// Add parents of matches if not matches themselves
|
||||
for (var id in this._searchParentIDs) {
|
||||
if (!this._searchItemIDs[id]) {
|
||||
var item = Zotero.Items.get(id);
|
||||
this._showItem(new Zotero.ItemTreeView.TreeRow(item, 0, false), added + 1); //item ref, before row
|
||||
added++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -667,8 +696,8 @@ Zotero.ItemTreeView.prototype.cycleHeader = function(column)
|
|||
var savedSelection = this.saveSelection();
|
||||
this.sort();
|
||||
this.rememberSelection(savedSelection);
|
||||
this.selection.selectEventsSuppressed = false;
|
||||
this._treebox.invalidate();
|
||||
this.selection.selectEventsSuppressed = false;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1053,10 +1082,12 @@ Zotero.ItemTreeView.prototype.setFilter = function(type, data) {
|
|||
this.sort();
|
||||
|
||||
this.rememberOpenState(savedOpenState);
|
||||
this.expandMatchParents();
|
||||
this.rememberFirstRow(savedFirstRow);
|
||||
this.rememberSelection(savedSelection);
|
||||
this.selection.selectEventsSuppressed = false;
|
||||
this._treebox.invalidate();
|
||||
this.selection.selectEventsSuppressed = false;
|
||||
|
||||
//Zotero.debug('Running callbacks in itemTreeView.setFilter()', 4);
|
||||
this._runCallbacks();
|
||||
}
|
||||
|
@ -1186,6 +1217,19 @@ Zotero.ItemTreeView.prototype.rememberOpenState = function(ids) {
|
|||
}
|
||||
|
||||
|
||||
Zotero.ItemTreeView.prototype.expandMatchParents = function () {
|
||||
// Expand parents of child matches
|
||||
if (this._searchMode) {
|
||||
var view = this._treebox.view;
|
||||
for (var id in this._searchParentIDs) {
|
||||
if (!view.isContainerOpen(this._itemRowMap[id])) {
|
||||
view.toggleOpenState(this._itemRowMap[id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Zotero.ItemTreeView.prototype.saveFirstRow = function() {
|
||||
var row = this._treebox.getFirstVisibleRow();
|
||||
if (row) {
|
||||
|
@ -1609,9 +1653,17 @@ Zotero.ItemTreeView.prototype.onDragOver = function (evt,dropdata,session) { }
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Zotero.ItemTreeView.prototype.isSeparator = function(row) { return false; }
|
||||
Zotero.ItemTreeView.prototype.getRowProperties = function(row, prop) { }
|
||||
Zotero.ItemTreeView.prototype.getRowProperties = function(row, prop) { }
|
||||
Zotero.ItemTreeView.prototype.getColumnProperties = function(col, prop) { }
|
||||
Zotero.ItemTreeView.prototype.getCellProperties = function(row, col, prop) { }
|
||||
|
||||
/* Mark items not matching search as context rows, displayed in gray */
|
||||
Zotero.ItemTreeView.prototype.getCellProperties = function(row, col, prop) {
|
||||
if (this._searchMode && !this._searchItemIDs[this._getItemAtRow(row).ref.getID()]) {
|
||||
var aServ = Components.classes["@mozilla.org/atom-service;1"].
|
||||
getService(Components.interfaces.nsIAtomService);
|
||||
prop.AppendElement(aServ.getAtom("contextRow"));
|
||||
}
|
||||
}
|
||||
|
||||
Zotero.ItemTreeView.TreeRow = function(ref, level, isOpen)
|
||||
{
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
*/
|
||||
|
||||
Zotero.Search = function(savedSearchID){
|
||||
this._scope = null;
|
||||
this._sql = null;
|
||||
this._sqlParams = null;
|
||||
this._maxSearchConditionID = 0;
|
||||
|
@ -231,6 +232,14 @@ Zotero.Search.prototype.addCondition = function(condition, operator, value, requ
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* Sets scope of search to the results of the passed Search object
|
||||
*/
|
||||
Zotero.Search.prototype.setScope = function (searchObj) {
|
||||
this._scope = searchObj;
|
||||
}
|
||||
|
||||
|
||||
Zotero.Search.prototype.updateCondition = function(searchConditionID, condition, operator, value, required){
|
||||
if (typeof this._conditions[searchConditionID] == 'undefined'){
|
||||
throw ('Invalid searchConditionID ' + searchConditionID + ' in updateCondition()');
|
||||
|
@ -314,7 +323,56 @@ Zotero.Search.prototype.search = function(){
|
|||
this._buildQuery();
|
||||
}
|
||||
|
||||
var ids = Zotero.DB.columnQuery(this._sql, this._sqlParams);
|
||||
if (this._scope) {
|
||||
// If subsearch has post-search filter, run and insert ids into temp table
|
||||
if (this._scope.hasPostSearchFilter()) {
|
||||
var ids = this._scope.search();
|
||||
if (!ids) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Zotero.DB.query("DROP TABLE IF EXISTS tmpSearchResults");
|
||||
|
||||
var sql = "CREATE TEMPORARY TABLE tmpSearchResults (itemID INT)";
|
||||
Zotero.DB.query(sql);
|
||||
var sql = "INSERT INTO tmpSearchResults VALUES (?)";
|
||||
var insertStatement = Zotero.DB.getStatement(sql);
|
||||
for (var i=0; i<ids.length; i++) {
|
||||
insertStatement.bindInt32Parameter(1, ids[i]);
|
||||
try {
|
||||
insertStatement.execute();
|
||||
}
|
||||
catch (e) {
|
||||
throw (Zotero.DB.getLastErrorString());
|
||||
}
|
||||
}
|
||||
insertStatement.reset();
|
||||
}
|
||||
// Otherwise, just copy to temp table directly
|
||||
else {
|
||||
var sql = "CREATE TEMPORARY TABLE tmpSearchResults AS "
|
||||
+ this._scope.getSQL();
|
||||
Zotero.DB.query(sql, this._scope.getSQLParams());
|
||||
}
|
||||
|
||||
var sql = "CREATE INDEX tmpSearchResults_itemID ON tmpSearchResults(itemID)";
|
||||
Zotero.DB.query(sql);
|
||||
|
||||
var sql = "SELECT itemID FROM items WHERE itemID IN (" + this._sql + ") "
|
||||
+ "AND ("
|
||||
+ "itemID IN (SELECT itemID FROM tmpSearchResults) OR "
|
||||
+ "itemID IN (SELECT itemID FROM itemAttachments"
|
||||
+ " WHERE sourceItemID IN (SELECT itemID FROM tmpSearchResults)) OR "
|
||||
+ "itemID IN (SELECT itemID FROM itemNotes"
|
||||
+ " WHERE sourceItemID IN (SELECT itemID FROM tmpSearchResults))"
|
||||
+ ")";
|
||||
var ids = Zotero.DB.columnQuery(sql, this._sqlParams);
|
||||
|
||||
Zotero.DB.query("DROP TABLE tmpSearchResults");
|
||||
}
|
||||
else {
|
||||
var ids = Zotero.DB.columnQuery(this._sql, this._sqlParams);
|
||||
}
|
||||
|
||||
//Zotero.debug('IDs from main search: ');
|
||||
//Zotero.debug(ids);
|
||||
|
@ -348,6 +406,10 @@ Zotero.Search.prototype.search = function(){
|
|||
// (a separate fulltext word search filtered by fulltext content)
|
||||
for each(var condition in this._conditions){
|
||||
if (condition['condition']=='fulltextContent'){
|
||||
if (this._scope) {
|
||||
throw ("Cannot perform fulltext content search with custom scope in Zotero.Search.search()");
|
||||
}
|
||||
|
||||
//Zotero.debug('Running subsearch against fulltext word index');
|
||||
|
||||
// Run a new search against the fulltext word index
|
||||
|
@ -900,10 +962,12 @@ Zotero.Search.prototype._buildQuery = function(){
|
|||
}
|
||||
// Keep non-required conditions separate if in ANY mode
|
||||
else if (!condition['required'] && joinMode == 'ANY') {
|
||||
var nonQSConditions = true;
|
||||
anySQL += condSQL + ' OR ';
|
||||
anySQLParams = anySQLParams.concat(condSQLParams);
|
||||
}
|
||||
else {
|
||||
var nonQSConditions = true;
|
||||
condSQL += ' AND ';
|
||||
sql += condSQL;
|
||||
sqlParams = sqlParams.concat(condSQLParams);
|
||||
|
@ -917,23 +981,20 @@ Zotero.Search.prototype._buildQuery = function(){
|
|||
sql = sql.substring(0, sql.length-4); // remove last ' OR '
|
||||
sql += ')';
|
||||
}
|
||||
else {
|
||||
else if (nonQSConditions) {
|
||||
sql = sql.substring(0, sql.length-5); // remove last ' AND '
|
||||
}
|
||||
else {
|
||||
sql = sql.substring(0, sql.length-7); // remove ' WHERE '
|
||||
}
|
||||
|
||||
// Add on quicksearch conditions -- this requires searching against
|
||||
// children, so we repeat the main query for note and attachment
|
||||
// sourceItemIDs before ANDing the quicksearch block
|
||||
// Add on quicksearch conditions
|
||||
if (quicksearchSQLSet) {
|
||||
sql = "SELECT itemID FROM items WHERE itemID IN (" + sql + " UNION "
|
||||
+ "SELECT itemID FROM itemNotes WHERE sourceItemID IN "
|
||||
+ "(" + sql + ") UNION "
|
||||
+ "SELECT itemID FROM itemAttachments WHERE sourceItemID IN "
|
||||
+ "(" + sql + ")) AND ((" + quicksearchSQLSet.join(') AND (') + "))";
|
||||
sql = "SELECT itemID FROM items WHERE itemID IN (" + sql + ") "
|
||||
+ "AND ((" + quicksearchSQLSet.join(') AND (') + "))";
|
||||
|
||||
sqlParams = sqlParams.concat(sqlParams).concat(sqlParams);
|
||||
for each(var p in quicksearchParamsSet) {
|
||||
sqlParams = sqlParams.concat(p);
|
||||
for (var k=0; k<quicksearchParamsSet.length; k++) {
|
||||
sqlParams = sqlParams.concat(quicksearchParamsSet[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
margin-right: 5px;
|
||||
}
|
||||
|
||||
/* Set by setHighlightedRows() and getCellProperties() in collectionTreeView.js) */
|
||||
/* Set by setHighlightedRows() and getRowProperties() in collectionTreeView.js) */
|
||||
#zotero-collections-tree treechildren::-moz-tree-row(highlighted)
|
||||
{
|
||||
background: #FFFF99;
|
||||
|
@ -59,6 +59,18 @@
|
|||
margin-right: 5px;
|
||||
}
|
||||
|
||||
|
||||
/* Style search results, display non-matches in gray */
|
||||
#zotero-items-tree treechildren::-moz-tree-cell-text(contextRow) {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
#zotero-items-tree treechildren::-moz-tree-cell-text(contextRow, selected, focus) {
|
||||
/* This is the default color, not the (platform-specific) highlight color, but at least it
|
||||
helps to differentiate when clicking on a context row. */
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
#zotero-items-pane
|
||||
{
|
||||
min-width: 300px;
|
||||
|
|
Loading…
Reference in New Issue
Block a user