Tag Selector pane -- filter the current items view by one or many tags

Will be getting a lot more functionality (e.g. renaming, deleting, maybe assigning of tags from the pane), some UI changes, and possibly some modified behavior (e.g. should it only show the available tags for the items that you're viewing, show all and let you use the interface to assign tags (say, by batch drag and drop), or have a checkbox to toggle between the two modes)

Other notes:

- Implemented as XBL binding, so should be reusable elsewhere if necessary
- Needs a better icon and possibly different icon placement
- Doesn't handle live updating of modified tags yet -- will need a Notifier target
- New methods Tags.getAll() and Tags.search()
- I really wish I'd created a ticket for this so I could check it off
This commit is contained in:
Dan Stillman 2006-11-09 13:33:49 +00:00
parent 7bb4091110
commit 3f59f808e3
9 changed files with 388 additions and 15 deletions

View file

@ -0,0 +1,216 @@
<?xml version="1.0"?>
<!--
***** BEGIN LICENSE BLOCK *****
Copyright (c) 2006 Center for History and New Media
George Mason University, Fairfax, Virginia, USA
http://chnm.gmu.edu
Licensed under the Educational Community License, Version 1.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.opensource.org/licenses/ecl1.php
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
***** END LICENSE BLOCK *****
-->
<?xml-stylesheet href="chrome://zotero/skin/overlay.css" type="text/css"?>
<!DOCTYPE bindings SYSTEM "chrome://zotero/locale/zotero.dtd">
<bindings xmlns="http://www.mozilla.org/xbl"
xmlns:xbl="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml">
<binding id="tag-selector">
<resources>
<stylesheet src="chrome://zotero/skin/bindings/tagselector.css"/>
</resources>
<implementation>
<field name="selection"/>
<constructor>
<![CDATA[
this.init();
]]>
</constructor>
<method name="init">
<body>
<![CDATA[
this.selection = {};
this.setTagList(Zotero.Tags.getAll());
]]>
</body>
</method>
<method name="setTagList">
<parameter name="tags"/>
<body>
<![CDATA[
var tagsToggleDiv = this.id('tags-toggle').firstChild;
// Save selection
var labels = tagsToggleDiv.getElementsByTagName('label');
for (var i=0; i<labels.length; i++){
//this.selection[labels[i].value] = labels[i].getAttribute('selected')=='true';
}
// Remove children
while (tagsToggleDiv.hasChildNodes()){
tagsToggleDiv.removeChild(tagsToggleDiv.firstChild);
}
// Regenerate list
for (var tagID in tags){
var label = document.createElement('label');
label.setAttribute('onclick', "this.parentNode.parentNode.parentNode.parentNode.handleTagClick(this)");
label.className = 'zotero-clicky';
label.setAttribute('value', tags[tagID]);
tagsToggleDiv.appendChild(label);
}
// Restore selection
var labels = tagsToggleDiv.getElementsByTagName('label');
for (var i=0; i<labels.length; i++){
if (this.selection[labels[i].value]){
labels[i].setAttribute('selected', 'true');
}
}
this.doCommand();
]]>
</body>
</method>
<method name="selectVisible">
<body>
<![CDATA[
var tagsToggleDiv = this.id('tags-toggle').firstChild;
var labels = tagsToggleDiv.getElementsByTagName('label');
for (var i=0; i<labels.length; i++){
labels[i].setAttribute('selected', 'true');
this.selection[labels[i].value] = true;
}
this.doCommand();
]]>
</body>
</method>
<method name="clearVisible">
<body>
<![CDATA[
var tagsToggleDiv = this.id('tags-toggle').firstChild;
var labels = tagsToggleDiv.getElementsByTagName('label');
for (var i=0; i<labels.length; i++){
labels[i].setAttribute('selected', 'false');
this.selection[labels[i].value] = false;
}
this.doCommand();
]]>
</body>
</method>
<method name="clearAll">
<body>
<![CDATA[
this.clearVisible();
this.selection = [];
this.doCommand();
]]>
</body>
</method>
<method name="handleKeyPress">
<parameter name="clear"/>
<body>
<![CDATA[
var t = document.commandDispatcher.focusedElement;
var textbox = t.parentNode.parentNode;
textbox.firstChild.hidden = (t.value == "");
if (clear != undefined){
if (clear){
t.value = '';
this.setTagList(Zotero.Tags.getAll());
return false;
}
else {
return true;
}
}
this.setTagList(Zotero.Tags.search(t.value));
return true;
]]>
</body>
</method>
<method name="handleTagClick">
<parameter name="label"/>
<body>
<![CDATA[
// Deselect
if (label.getAttribute('selected')=='true'){
this.selection[label.value] = false;
label.setAttribute('selected', 'false');
}
// Select
else {
this.selection[label.value] = true;
label.setAttribute('selected', 'true');
}
this.doCommand();
]]>
</body>
</method>
<method name="id">
<parameter name="id"/>
<body>
<![CDATA[
return document.getAnonymousNodes(this)[0].getElementsByAttribute('id',id)[0];
]]>
</body>
</method>
</implementation>
<content>
<xul:groupbox flex="1">
<xul:vbox id="tags-toggle" flex="1">
<html:div/>
</xul:vbox>
<xul:hbox>
<xul:label value="&zotero.tagSelector.filter;"/>
<xul:textbox id="tags-search" flex="1" type="timed" timeout="250" dir="reverse"
oncommand="this.parentNode.parentNode.parentNode.handleKeyPress()"
onkeypress="this.parentNode.parentNode.parentNode.handleKeyPress(event.keyCode == event.DOM_VK_ESCAPE)">
<xul:toolbarbutton id="search-cancel"
oncommand="this.parentNode.focus(); this.parentNode.value='';" hidden="true"/>
</xul:textbox>
</xul:hbox>
<xul:hbox>
<xul:toolbarbutton label="&zotero.tagSelector.selectVisible;" class="zotero-clicky"
oncommand="this.parentNode.parentNode.parentNode.selectVisible()"/>
<xul:toolbarbutton label="&zotero.tagSelector.clearVisible;" class="zotero-clicky"
oncommand="this.parentNode.parentNode.parentNode.clearVisible()"/>
<xul:toolbarbutton label="&zotero.tagSelector.clearAll;" class="zotero-clicky"
oncommand="this.parentNode.parentNode.parentNode.clearAll()"/>
</xul:hbox>
</xul:groupbox>
</content>
</binding>
</bindings>

View file

@ -36,6 +36,9 @@ var ZoteroPane = new function()
this.newItem = newItem;
this.newCollection = newCollection;
this.newSearch = newSearch;
this.toggleTagSelector = toggleTagSelector;
this.getTagSelection = getTagSelection;
this.updateTagFilter = updateTagFilter;
this.onCollectionSelected = onCollectionSelected;
this.itemSelected = itemSelected;
this.deleteSelectedItem = deleteSelectedItem;
@ -126,8 +129,8 @@ var ZoteroPane = new function()
moreMenu.appendChild(menuitem);
}
var menu = document.getElementById("contentAreaContextMenu");
menu.addEventListener("popupshowing", ZoteroPane.contextPopupShowing, false);
var menu = document.getElementById("contentAreaContextMenu");
menu.addEventListener("popupshowing", ZoteroPane.contextPopupShowing, false);
}
/*
@ -233,6 +236,40 @@ var ZoteroPane = new function()
window.openDialog('chrome://zotero/content/searchDialog.xul','','chrome,modal',io);
}
function toggleTagSelector(){
var tagSelector = document.getElementById('zotero-tag-selector');
var collapsed = tagSelector.getAttribute('collapsed')=='true';
// If hiding, clear selection
if (!collapsed){
tagSelector.init();
}
tagSelector.setAttribute('collapsed', !collapsed);
}
function getTagSelection(){
var tagSelector = document.getElementById('zotero-tag-selector');
return tagSelector.selection;
}
function updateTagFilter(){
if (itemsView)
{
itemsView.unregister();
}
if (collectionsView){
var itemgroup = collectionsView._getItemAtRow(collectionsView.selection.currentIndex);
itemgroup.setTags(getTagSelection());
itemsView = new Zotero.ItemTreeView(itemgroup);
document.getElementById('zotero-items-tree').view = itemsView;
}
}
function onCollectionSelected()
{
if(itemsView)
@ -244,6 +281,7 @@ var ZoteroPane = new function()
{
var itemgroup = collectionsView._getItemAtRow(collectionsView.selection.currentIndex);
itemgroup.setSearch('');
itemgroup.setTags(getTagSelection());
itemsView = new Zotero.ItemTreeView(itemgroup);
document.getElementById('zotero-items-tree').view = itemsView;
@ -485,6 +523,7 @@ var ZoteroPane = new function()
}
}
function search()
{
if(itemsView)

View file

@ -24,7 +24,8 @@
<!DOCTYPE window SYSTEM "chrome://zotero/locale/zotero.dtd">
<overlay id="zotero"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml">
<?xul-overlay href="itemPane.xul" ?>
<!-- Include the global XPCOM object -->
@ -34,7 +35,6 @@
<script src="fileInterface.js"/>
<commandset id="mainCommandSet">
<command id="cmd_zotero_newItem" oncommand="ZoteroPane.newItem(2);"/>
<command id="cmd_zotero_newCollection" oncommand="ZoteroPane.newCollection();"/>
<command id="cmd_zotero_search" oncommand="ZoteroPane.search();"/>
</commandset>
@ -92,6 +92,7 @@
<toolbarbutton id="zotero-tb-collection-add" tooltiptext="&zotero.toolbar.newCollection.label;" command="cmd_zotero_newCollection"/>
<toolbarbutton id="zotero-tb-collection-addsearch" tooltiptext="&zotero.toolbar.newSavedSearch.label;" oncommand="ZoteroPane.newSearch();"/>
<spacer flex="1"/>
<toolbarbutton id="zotero-tb-tag-selector" tooltiptext="&zotero.toolbar.tagSelector.label;" oncommand="ZoteroPane.toggleTagSelector()"/>
<toolbarbutton id="zotero-tb-actions-menu" tooltiptext="&zotero.toolbar.actions.label;" type="menu">
<menupopup>
<menuitem label="&zotero.toolbar.import.label;" oncommand="Zotero_File_Interface.importFile();"/>
@ -117,6 +118,8 @@
</treecols>
<treechildren/>
</tree>
<splitter id="zotero-tags-splitter"/>
<zoterotagselector id="zotero-tag-selector" persist="height,collapsed" oncommand="ZoteroPane.updateTagFilter()"/>
</vbox>
<splitter id="zotero-tree-splitter" resizebefore="closest" resizeafter="closest"/>
<vbox id="zotero-items-pane" persist="width" flex="1">

View file

@ -699,6 +699,14 @@ Zotero.ItemGroup.prototype.getChildItems = function()
}
}
if (this.tags){
for (var tag in this.tags){
if (this.tags[tag]){
s.addCondition('tag', 'is', tag);
}
}
}
var ids = s.search();
return Zotero.Items.get(ids);
}
@ -706,4 +714,9 @@ Zotero.ItemGroup.prototype.getChildItems = function()
Zotero.ItemGroup.prototype.setSearch = function(searchText)
{
this.searchText = searchText;
}
Zotero.ItemGroup.prototype.setTags = function(tags)
{
this.tags = tags;
}

View file

@ -2858,6 +2858,8 @@ Zotero.Tags = new function(){
this.getName = getName;
this.getID = getID;
this.getAll = getAll;
this.search = search;
this.add = add;
this.purge = purge;
@ -2900,6 +2902,32 @@ Zotero.Tags = new function(){
}
/**
* Get all tags indexed by tagID
*/
function getAll(){
var sql = 'SELECT tagID, tag FROM tags ORDER BY tag COLLATE NOCASE';
var tags = Zotero.DB.query(sql);
var indexed = {};
for (var i in tags){
indexed[tags[i]['tagID']] = tags[i]['tag'];
}
return indexed;
}
function search(str){
var sql = 'SELECT tagID, tag FROM tags WHERE tag LIKE ? '
+ 'ORDER BY tag COLLATE NOCASE';
var tags = Zotero.DB.query(sql, '%' + str + '%');
var indexed = {};
for (var i in tags){
indexed[tags[i]['tagID']] = tags[i]['tag'];
}
return indexed;
}
/*
* Add a new tag to the database
*

View file

@ -29,6 +29,7 @@
<!ENTITY zotero.toolbar.removeItem.label "Remove Item...">
<!ENTITY zotero.toolbar.newCollection.label "New Collection...">
<!ENTITY zotero.toolbar.newSavedSearch.label "New Saved Search...">
<!ENTITY zotero.toolbar.tagSelector.label "Show/Hide Tag Selector">
<!ENTITY zotero.toolbar.renameCollection.label "Rename Collection...">
<!ENTITY zotero.toolbar.removeCollection.label "Remove Collection...">
<!ENTITY zotero.toolbar.exportCollection.label "Export Collection...">
@ -49,7 +50,6 @@
<!ENTITY zotero.toolbar.openURL.label "Locate">
<!ENTITY zotero.toolbar.openURL.tooltip "Find through your local library">
<!ENTITY zotero.item.add "Add">
<!ENTITY zotero.item.attachment.file.show "Show File">
@ -59,6 +59,11 @@
<!ENTITY zotero.toolbar.attachment.weblink "Save Link to Current Page">
<!ENTITY zotero.toolbar.attachment.snapshot "Take Snapshot of Current Page">
<!ENTITY zotero.tagSelector.filter "Filter:">
<!ENTITY zotero.tagSelector.selectVisible "Select Visible">
<!ENTITY zotero.tagSelector.clearVisible "Clear Visible">
<!ENTITY zotero.tagSelector.clearAll "Clear All">
<!ENTITY zotero.selectitems.title "Select Items">
<!ENTITY zotero.selectitems.intro.label "Select which items you'd like to add to your library">
<!ENTITY zotero.selectitems.cancel.label "Cancel">

View file

@ -0,0 +1,54 @@
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
@namespace html url("http://www.w3.org/1999/xhtml");
/* Tag selector */
groupbox
{
overflow: auto;
min-height: 153px;
}
vbox
{
overflow: auto;
margin-bottom: 10px;
}
html|div
{
position: relative;
}
label
{
margin-right: 5px;
padding: 2px 5px;
}
label[selected=true]
{
background: #a9c6f0 !important;
}
hbox
{
-moz-box-align: baseline;
-moz-box-pack: center;
}
#search-cancel
{
margin: 0px;
padding: 0px;
cursor: default;
list-style-image: url('chrome://zotero/skin/search-cancel.png');
}
toolbarbutton.zotero-clicky
{
margin:3px 5px;
padding:1px;
height:1em;
}

View file

@ -19,11 +19,16 @@
width: 150px;
}
#zotero-collections-tree {
min-height: 80px;
}
#zotero-collections-tree treechildren::-moz-tree-image
{
margin-right: 5px;
}
#zotero-pane splitter
{
background: #f5f5f5;
@ -70,17 +75,14 @@
#zotero-tb-collection-add
{
list-style-image: url('chrome://zotero/skin/toolbar-collection-add.png');
margin-top: -2px;
}
#zotero-tb-collection-rename
#zotero-tb-tag-selector
{
list-style-image: url('chrome://zotero/skin/toolbar-collection-edit.png');
list-style-image: url(chrome://zotero/skin/tag.png);
}
#zotero-tb-collection-rename[disabled="true"]
{
list-style-image: url('chrome://zotero/skin/toolbar-collection-edit-gray.png');
}
/* These fix a rendering bug in Fx2.0b2 */
@ -172,7 +174,6 @@
#zotero-tb-collection-addsearch
{
list-style-image: url('chrome://zotero/skin/treesource-search.png');
margin-bottom:-5px;
}
#zotero-tb-search-cancel

View file

@ -51,7 +51,6 @@ tagsbox
-moz-binding: url('chrome://zotero/content/bindings/tagsbox.xml#tags-box');
}
tagsbox row
{
-moz-box-align:center;
@ -68,6 +67,12 @@ zoterosearch
width:60em;
}
zoterotagselector
{
-moz-binding: url('chrome://zotero/content/bindings/tagselector.xml#tag-selector');
}
searchcondition
{
-moz-binding: url('chrome://zotero/content/bindings/zoterosearch.xml#search-condition');
@ -207,12 +212,21 @@ zoterosearchagefield textbox
padding-right: 4px;
}
.zotero-clicky:not([disabled=true]):hover
.zotero-clicky:not([disabled=true]):hover,
.zotero-clicky:not([disabled=true]):hover .toolbarbutton-text
{
color: white;
background: #666666;
background: #666;
}
.zotero-clicky:not([disabled=true]):active,
.zotero-clicky:not([disabled=true]):active .toolbarbutton-text
{
color: white;
background: #333;
}
#zotero-progress-box, #zotero-progress-box
{
border: 2px solid #7a0000;