From d2f028d797c5c0ae3d77d54a5bbabaa5cd4a961b Mon Sep 17 00:00:00 2001 From: Dan Stillman Date: Sun, 3 Mar 2013 03:38:02 -0500 Subject: [PATCH] Tag colors, synced settings, and (stopgap) silent DB upgrades - New tag colors support, with the ability to assign colors to up to 6 tags per library. Tags with colors assigned will show up at the top of the tag selector and can be added to (and removed from) selected items by pressing the 1-6 keys on the keyboard. The tags will show up as color swatches before an item's title in the items list. - Synced settings, with Notifier triggers when they change and accessible via the API (currently restricted on the server to 'tagColors', but available for other things upon request) - Silent DB upgrades for backwards-compatible changes. We'll do something fancier with async DB queries in 4.0, but this will work for changes that can be made without breaking compatibility with older clients, like the creation of new tables. The 'userdata' value is capped at 76, while further increments go to 'userdata2'. TODO: - Try to avoid jitter when redrawing swatches - Optimize tag color images for retina displays - Redo attachment dots in flat style? - Clear all colors from an item with 0 (as in Thunderbird), but I don't think we can do this without undo --- .../zotero/bindings/customcolorpicker.xml | 136 ++++ chrome/content/zotero/bindings/tagsbox.xml | 80 ++- .../content/zotero/bindings/tagselector.xml | 536 +++++++------- chrome/content/zotero/tagColorChooser.js | 154 +++++ chrome/content/zotero/tagColorChooser.xul | 63 ++ chrome/content/zotero/xpcom/data/group.js | 4 + chrome/content/zotero/xpcom/data/item.js | 2 + chrome/content/zotero/xpcom/data/tag.js | 2 +- chrome/content/zotero/xpcom/data/tags.js | 653 ++++++++++++++---- chrome/content/zotero/xpcom/file.js | 21 + chrome/content/zotero/xpcom/itemTreeView.js | 190 +++-- chrome/content/zotero/xpcom/notifier.js | 2 +- chrome/content/zotero/xpcom/schema.js | 84 ++- chrome/content/zotero/xpcom/sync.js | 65 +- chrome/content/zotero/xpcom/syncedSettings.js | 115 +++ chrome/content/zotero/xpcom/zotero.js | 2 + chrome/content/zotero/zoteroPane.xul | 3 +- chrome/locale/en-US/zotero/zotero.dtd | 7 + chrome/locale/en-US/zotero/zotero.properties | 4 + .../zotero/bindings/customcolorpicker.css | 24 + chrome/skin/default/zotero/overlay.css | 143 ---- .../skin/default/zotero/tagColorChooser.css | 8 + chrome/skin/default/zotero/zotero.css | 9 +- components/zotero-service.js | 1 + resource/schema/system.sql | 1 + resource/schema/userdata.sql | 13 +- 26 files changed, 1706 insertions(+), 616 deletions(-) create mode 100644 chrome/content/zotero/bindings/customcolorpicker.xml create mode 100644 chrome/content/zotero/tagColorChooser.js create mode 100644 chrome/content/zotero/tagColorChooser.xul create mode 100644 chrome/content/zotero/xpcom/syncedSettings.js create mode 100644 chrome/skin/default/zotero/bindings/customcolorpicker.css create mode 100644 chrome/skin/default/zotero/tagColorChooser.css diff --git a/chrome/content/zotero/bindings/customcolorpicker.xml b/chrome/content/zotero/bindings/customcolorpicker.xml new file mode 100644 index 0000000000..a420691369 --- /dev/null +++ b/chrome/content/zotero/bindings/customcolorpicker.xml @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + [ + 'L#FFFFFF','L#FFCCCC','L#FFCC99','L#FFFF99','L#FFFFCC','L#99FF99','L#99FFFF','L#CCFFFF','L#CCCCFF','L#FFCCFF', + '#CCCCCC','#FF6666','#FF9966','L#FFFF66','L#FFFF33','L#66FF99','L#33FFFF','L#66FFFF','#9999FF','#FF99FF', + '#C0C0C0','#FF0000','#FF9900','#FFCC66','L#FFFF00','L#33FF33','#66CCCC','#33CCFF','#6666CC','#CC66CC', + '#999999','#CC0000','#FF6600','#FFCC33','#FFCC00','#33CC00','#00CCCC','#3366FF','#6633FF','#CC33CC', + '#666666','#990000','#CC6600','#CC9933','#999900','#009900','#339999','#3333FF','#6600CC','#993399', + '#333333','#660000','#993300','#996633','#666600','#006600','#336666','#000099','#333399','#663366', + '#000000','#330000','#663300','#663333','#333300','#003300','#003333','#000066','#330099','#330033' + ] + + 10 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/chrome/content/zotero/bindings/tagsbox.xml b/chrome/content/zotero/bindings/tagsbox.xml index 45f7677fd3..a6c7017216 100644 --- a/chrome/content/zotero/bindings/tagsbox.xml +++ b/chrome/content/zotero/bindings/tagsbox.xml @@ -37,6 +37,7 @@ + "view" @@ -109,38 +110,64 @@ if (this.hasAttribute('mode')) { this.mode = this.getAttribute('mode'); } + + this._notifierID = Zotero.Notifier.registerObserver(this, ['setting']); ]]> + + + + + + + + + + + + + + + + @@ -264,8 +291,13 @@ } // Tag color - if (color = this._tagColors[valueText]) { - valueElement.setAttribute('style', 'color:' + this._tagColors[valueText]); + let color = this._tagColors[valueText]; + if (color) { + valueElement.setAttribute( + 'style', + 'color:' + this._tagColors[valueText].color + '; ' + + 'font-weight: bold' + ); } return valueElement; diff --git a/chrome/content/zotero/bindings/tagselector.xml b/chrome/content/zotero/bindings/tagselector.xml index 8cfe3b6daf..45d63bccf7 100644 --- a/chrome/content/zotero/bindings/tagselector.xml +++ b/chrome/content/zotero/bindings/tagselector.xml @@ -27,8 +27,7 @@ + xmlns:xbl="http://www.mozilla.org/xbl"> @@ -77,6 +76,9 @@ @@ -215,211 +217,211 @@ var empty = true; var tagsToggleBox = this.id('tags-toggle'); - - if (fetch || this._dirty) { - this._tags = Zotero.Tags.getAll(this._types, this.libraryID); - - // Remove children - tagsToggleBox.textContent = ""; - - var me = this, - onTagClick = function(event) { me.handleTagClick(event, this) }, - lastTag; - for (var tagID in this._tags) { - var tagInfo = this._tags[tagID], - tagName = tagInfo.name; - // If the last tag was the same, add this tagID and tagType to it - if(lastTag && lastTag.value === tagName) { - lastTag.setAttribute('tagID', lastTag.getAttribute('tagID') + '-' + tagID); - lastTag.setAttribute('tagType', lastTag.getAttribute('tagType') + '-' + tagName.type); - continue; - } + var self = this; + Zotero.Tags.getColors(this.libraryID) + .then(function (tagColors) { + if (fetch || self._dirty) { + self._tags = Zotero.Tags.getAll(self._types, self.libraryID); - lastTag = document.createElement('label'); - lastTag.addEventListener('click', onTagClick, false); - lastTag.className = 'zotero-clicky'; + // Remove children + tagsToggleBox.textContent = ""; - - lastTag.setAttribute('value', tagName); - lastTag.setAttribute('tagID', tagID); - lastTag.setAttribute('tagType', tagInfo.type); - if (this.editable) { - lastTag.setAttribute('context', 'tag-menu'); - lastTag.addEventListener('dragover', this.dragObserver.onDragOver, false); - lastTag.addEventListener('dragexit', this.dragObserver.onDragExit, false); - lastTag.addEventListener('drop', this.dragObserver.onDrop, true); - } - tagsToggleBox.appendChild(lastTag); - } - this._dirty = false; - } - - // Set attributes - var labels = tagsToggleBox.getElementsByTagName('label'); - var tagColors = Zotero.Tags.getColors(); - for (var i=0; i=0; i--) { + tagsToggleBox.insertBefore(colorTags[positions[i]], tagsToggleBox.firstChild); + } + + //start tag cloud code + + var tagCloud = Zotero.Prefs.get('tagCloud'); + + if(tagCloud) { + var labels = tagsToggleBox.getElementsByTagName('label'); + + //loop through displayed labels and find number of linked items + var numlinked= []; + for (var i=0; i @@ -437,6 +439,7 @@ + @@ -719,43 +733,84 @@ - + + + + + + + + + + + - = Zotero.Tags.MAX_COLORED_TAGS && !tagColors[io.name]) { + var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] + .getService(Components.interfaces.nsIPromptService); + ps.alert(null, "", Zotero.getString('pane.tagSelector.maxColoredTags', Zotero.Tags.MAX_COLORED_TAGS)); + return; } - } + + window.openDialog( + 'chrome://zotero/content/tagColorChooser.xul', + "zotero-tagSelector-colorChooser", + "chrome,modal,centerscreen", io + ); + + // Dialog cancel + if (typeof io.color == 'undefined') { + return; + } + + return Zotero.Tags.setColor(self.libraryID, io.name, io.color, io.position); + }) + .done(); ]]> + - - - - - - - + + + + + + - - - + + - + - - + + - - - - - + + - - + - - - - - - + + + + + diff --git a/chrome/content/zotero/tagColorChooser.js b/chrome/content/zotero/tagColorChooser.js new file mode 100644 index 0000000000..3ee3f6d3e0 --- /dev/null +++ b/chrome/content/zotero/tagColorChooser.js @@ -0,0 +1,154 @@ +/* + ***** BEGIN LICENSE BLOCK ***** + + Copyright © 2013 Center for History and New Media + George Mason University, Fairfax, Virginia, USA + http://zotero.org + + This file is part of Zotero. + + Zotero is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Zotero is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with Zotero. If not, see . + + ***** END LICENSE BLOCK ***** +*/ + +"use strict"; +var _io; + +var Zotero_Tag_Color_Chooser = new function() { + this.init = function () { + // Set font size from pref + Zotero.setFontSize(document.getElementById("tag-color-chooser-container")); + + if (window.arguments && window.arguments.length) { + _io = window.arguments[0]; + if (_io.wrappedJSObject) _io = _io.wrappedJSObject; + } + if (typeof _io.libraryID == 'undefined') throw new Error("libraryID not set"); + if (typeof _io.name == 'undefined' || _io.name === "") throw new Error("name not set"); + + window.sizeToContent(); + + var dialog = document.getElementById('tag-color-chooser'); + var colorPicker = document.getElementById('color-picker'); + var tagPosition = document.getElementById('tag-position'); + + colorPicker.setAttribute('cols', 3); + colorPicker.setAttribute('tileWidth', 24); + colorPicker.setAttribute('tileHeight', 24); + colorPicker.colors = [ + '#990000', '#CC9933', '#FF9900', + '#FFCC00', '#007439', '#1049A9', + '#9999FF', '#CC66CC', '#993399' + ]; + + var maxTags = document.getElementById('max-tags'); + maxTags.value = Zotero.getString('tagColorChooser.maxTags', Zotero.Tags.MAX_COLORED_TAGS); + + var self = this; + Zotero.Tags.getColors(_io.libraryID) + .then(function (tagColors) { + var colorData = tagColors[_io.name]; + + // Color + if (colorData) { + colorPicker.color = colorData.color; + dialog.buttons = "extra1,cancel,accept"; + } + else { + // Get unused color at random + var usedColors = []; + for (var i in tagColors) { + usedColors.push(tagColors[i].color); + } + var unusedColors = Zotero.Utilities.arrayDiff( + colorPicker.colors, usedColors + ); + var color = unusedColors[Zotero.Utilities.rand(0, unusedColors.length - 1)]; + colorPicker.color = color; + dialog.buttons = "cancel,accept"; + } + colorPicker.setAttribute('disabled', 'false'); + + var numColors = Object.keys(tagColors).length; + var max = colorData ? numColors : numColors + 1; + + // Position + for (let i=1; i<=max; i++) { + tagPosition.appendItem(i, i-1); + } + if (numColors) { + tagPosition.setAttribute('disabled', 'false'); + if (colorData) { + tagPosition.selectedIndex = colorData.position; + } + // If no color currently, default to end + else { + tagPosition.selectedIndex = numColors; + } + } + // If no colors currently, only position "1" is available + else { + tagPosition.selectedIndex = 0; + } + + self.onPositionChange(); + window.sizeToContent(); + }) + .catch(function (e) { + Zotero.debug(e, 1); + Components.utils.reportError(e); + dialog.cancelDialog(); + }) + .done(); + }; + + + this.onPositionChange = function () { + var tagPosition = document.getElementById('tag-position'); + var instructions = document.getElementById('number-key-instructions'); + + while (instructions.hasChildNodes()) { + instructions.removeChild(instructions.firstChild); + } + + var msg = Zotero.getString('tagColorChooser.numberKeyInstructions'); + var matches = msg.match(/(.+)\$NUMBER(.+)/); + + var num = document.createElement('label'); + num.id = 'number-key'; + num.setAttribute('value', parseInt(tagPosition.value) + 1); + + instructions.appendChild(document.createTextNode(matches[1])); + instructions.appendChild(num); + instructions.appendChild(document.createTextNode(matches[2])); + }; + + + this.onDialogAccept = function () { + var colorPicker = document.getElementById('color-picker'); + var tagPosition = document.getElementById('tag-position'); + _io.color = colorPicker.color; + _io.position = tagPosition.value; + }; + + + this.onDialogCancel = function () {}; + + + this.onDialogRemoveColor = function () { + _io.color = false; + window.close(); + }; +}; diff --git a/chrome/content/zotero/tagColorChooser.xul b/chrome/content/zotero/tagColorChooser.xul new file mode 100644 index 0000000000..efe8f3bec8 --- /dev/null +++ b/chrome/content/zotero/tagColorChooser.xul @@ -0,0 +1,63 @@ + + + + + + + + + +