www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | Submodules | README | LICENSE

commit 8ad23d7eea79f19676a199fc48d1966326d6d2a3
parent 403663664c9efece9a1171028302782704e4cd23
Author: Dan Stillman <dstillman@zotero.org>
Date:   Mon, 29 Dec 2008 06:18:41 +0000

- Abstract attachment info pane into XBL binding
- Add attachment conflict resolution


Diffstat:
Achrome/content/zotero/bindings/attachmentbox.xml | 430+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mchrome/content/zotero/bindings/merge.xml | 34++++++++++++++++++++++++++++++++++
Mchrome/content/zotero/bindings/noteeditor.xml | 8++++----
Mchrome/content/zotero/bindings/styled-textbox.xml | 68++++++++++++++++++++++++++++++++++++++++----------------------------
Mchrome/content/zotero/merge.js | 32+++++++++++++++++---------------
Mchrome/content/zotero/overlay.js | 291+++++++++++++------------------------------------------------------------------
Mchrome/content/zotero/overlay.xul | 20+-------------------
Mchrome/content/zotero/xpcom/data/item.js | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
Mchrome/content/zotero/xpcom/sync.js | 6------
Achrome/skin/default/zotero/bindings/attachmentbox.css | 36++++++++++++++++++++++++++++++++++++
Mchrome/skin/default/zotero/overlay.css | 46----------------------------------------------
Mchrome/skin/default/zotero/zotero.css | 6++++++
12 files changed, 672 insertions(+), 388 deletions(-)

diff --git a/chrome/content/zotero/bindings/attachmentbox.xml b/chrome/content/zotero/bindings/attachmentbox.xml @@ -0,0 +1,430 @@ +<?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 ***** +--> + +<!DOCTYPE bindings SYSTEM "chrome://zotero/locale/zotero.dtd"> +<!-- <!DOCTYPE bindings SYSTEM "chrome://zotero/locale/attachmentbox.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"> + + <binding id="attachment-box"> + <resources> + <stylesheet src="chrome://zotero/skin/bindings/attachmentbox.css"/> + </resources> + + <implementation> + <!-- + Public properties + --> + <field name="editable">false</field> + <field name="displayGoButtons">false</field> + <field name="clickableLink">false</field> + <field name="displayButton">false</field> + + <field name="buttonCaption"/> + <field name="clickHandler"/> + + <!-- Modes are predefined settings groups for particular tasks --> + <field name="_mode">"view"</field> + <property name="mode" onget="return this._mode;"> + <setter> + <![CDATA[ + this.editable = false; + this.displayGoButtons = false; + this.clickableLink = false; + this.displayIndexed = false; + this.displayPages = false; + + switch (val) { + case 'view': + this.displayGoButtons = true; + this.clickableLink = true; + this.displayIndexed = true; + this.displayPages = true; + break; + + case 'edit': + this.editable = true; + this.displayGoButtons = true; + this.clickableLink = true; + this.displayIndexed = true; + this.displayPages = true; + break; + + case 'merge': + this.displayButton = true; + break; + + case 'mergeedit': + break; + + default: + throw ("Invalid mode '" + val + "' in attachmentbox.xml"); + } + + this._mode = val; + document.getAnonymousNodes(this)[0].setAttribute('mode', val); + ]]> + </setter> + </property> + + <field name="_item"/> + <property name="item" + onget="return this._item;" + onset="this._item = val; this.refresh();"> + </property> + + <!-- .ref is an alias for .item --> + <property name="ref" + onget="return this._item;" + onset="this._item = val; this.refresh();"> + </property> + + + <!-- Private properties --> + + <method name="refresh"> + <body> + <![CDATA[ + Zotero.debug('Refreshing attachment box'); + + var attachmentBox = document.getAnonymousNodes(this)[0]; + var title = this._id('title'); + var goButtons = this._id('go-buttons'); + var viewButton = this._id('view'); + var showButton = this._id('show'); + var urlField = this._id('url'); + var accessed = this._id('accessed'); + var pagesRow = this._id('pages'); + var indexBox = this._id('index-box'); + var selectButton = this._id('select-button'); + + // DEBUG: this is annoying -- we really want to use an abstracted + // version of createValueElement() from itemPane.js + // (ideally in an XBL binding) + + // Wrap title to multiple lines if necessary + while (title.hasChildNodes()) { + title.removeChild(title.firstChild); + } + var val = this.item.getField('title'); + + var firstSpace = val.indexOf(" "); + // Crop long uninterrupted text + if ((firstSpace == -1 && val.length > 29 ) || firstSpace > 29) { + title.setAttribute('crop', 'end'); + title.setAttribute('value', val); + } + // Create a <description> element, essentially + else { + title.removeAttribute('value'); + title.appendChild(document.createTextNode(val)); + } + + if (this.editable) { + title.className = 'zotero-clicky'; + + // For the time being, use a silly little popup + title.addEventListener('click', this.editTitle, false); + } + + // View and Show buttons + if (this.displayGoButtons) { + goButtons.hidden = false; + + var itemID = this.item.id; + var noLocateOnMissing = !this.editable; + viewButton.onclick = function (event) { + ZoteroPane.viewAttachment(itemID, event, noLocateOnMissing) + } + showButton.onclick = function (event) { + ZoteroPane.showAttachmentInFilesystem(itemID, noLocateOnMissing) + } + } + else { + goButtons.hidden = true; + } + + var isImportedURL = this.item.attachmentLinkMode == + Zotero.Attachments.LINK_MODE_IMPORTED_URL; + + // Metadata for URL's + if (this.item.attachmentLinkMode == Zotero.Attachments.LINK_MODE_LINKED_URL + || isImportedURL) { + // "View Page"/"View Snapshot" label + if (isImportedURL) { + var str = Zotero.getString('pane.item.attachments.view.snapshot'); + } + else { + var str = Zotero.getString('pane.item.attachments.view.link'); + } + + showButton.setAttribute('hidden', !isImportedURL); + + // URL + var urlSpec = this.item.getField('url'); + urlField.setAttribute('value', urlSpec); + urlField.setAttribute('hidden', false); + if (this.clickableLink) { + urlField.onclick = function (event) { + ZoteroPane.loadURI(this.value, event) + }; + urlField.className = 'text-link'; + } + else { + urlField.className = ''; + } + + // Access date + accessed.setAttribute('value', + Zotero.getString('itemFields.accessDate') + ': ' + + Zotero.Date.sqlToDate(this.item.getField('accessDate'), true).toLocaleString()); + accessed.setAttribute('hidden', false); + } + // Metadata for files + else { + var str = Zotero.getString('pane.item.attachments.view.file'); + showButton.setAttribute('hidden', false); + urlField.setAttribute('hidden', true); + accessed.setAttribute('hidden', true); + } + + viewButton.setAttribute('label', str); + + // Page count + if (this.displayPages) { + var pages = Zotero.Fulltext.getPages(this.item.id); + var pages = pages ? pages.total : null; + if (pages) { + var str = Zotero.getString('itemFields.pages') + ': ' + pages; + pagesRow.setAttribute('value', str); + pagesRow.setAttribute('hidden', false); + } + else { + pagesRow.setAttribute('hidden', true); + } + } + else { + pagesRow.setAttribute('hidden', true); + } + + // Full-text index information + if (this.displayIndexed) { + this.updateItemIndexedState(); + indexBox.hidden = false; + } + else { + indexBox.hidden = true; + } + + // Note editor + var noteEditor = this._id('note-editor'); + // Don't make note editable (at least for now) + if (this.mode == 'merge' || this.mode == 'mergeedit') { + noteEditor.mode = 'merge'; + noteEditor.displayButton = false; + } + else { + noteEditor.mode = this.mode; + } + noteEditor.parent = null; + noteEditor.item = this.item; + + if (this.displayButton) { + selectButton.label = this.buttonCaption; + selectButton.hidden = false; + selectButton.setAttribute('oncommand', + 'document.getBindingParent(this).clickHandler(this)'); + } + else { + selectButton.hidden = true; + } + ]]> + </body> + </method> + + + <method name="editTitle"> + <body> + <![CDATA[ + var item = document.getBindingParent(this).item; + var oldTitle = item.getField('title'); + + var nsIPS = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] + .getService(Components.interfaces.nsIPromptService); + + var newTitle = { value: oldTitle }; + var checkState = { value: Zotero.Prefs.get('lastRenameAssociatedFile') }; + + while (true) { + // Don't show "Rename associated file" option for + // linked URLs + if (item.attachmentLinkMode == + Zotero.Attachments.LINK_MODE_LINKED_URL) { + var result = nsIPS.prompt( + window, + '', + Zotero.getString('pane.item.attachments.rename.title'), + newTitle, + null, + {} + ); + + // If they hit cancel or left it blank + if (!result || !newTitle.value) { + return; + } + + break; + } + + var result = nsIPS.prompt( + window, + '', + Zotero.getString('pane.item.attachments.rename.title'), + newTitle, + Zotero.getString('pane.item.attachments.rename.renameAssociatedFile'), + checkState + ); + + // If they hit cancel or left it blank + if (!result || !newTitle.value) { + return; + } + + Zotero.Prefs.set('lastRenameAssociatedFile', checkState.value); + + // Rename associated file + if (checkState.value) { + var renamed = item.renameAttachmentFile(newTitle.value); + if (renamed == -1) { + var confirmed = confirm(newTitle.value + ' exists. Overwrite existing file?'); + if (confirmed) { + item.renameAttachmentFile(newTitle.value, true); + break; + } + // If they said not to overwrite existing file, + // start again + continue; + } + else if (renamed == -2 || !renamed) { + alert(Zotero.getString('pane.item.attachments.rename.error')); + return; + } + } + + break; + } + + if (newTitle.value != oldTitle) { + item.setField('title', newTitle.value); + item.save(); + } + ]]> + </body> + </method> + + + <!-- + Update Indexed: (Yes|No|Partial) line + --> + <method name="updateItemIndexedState"> + <body> + <![CDATA[ + var indexBox = this._id('index-box'); + var indexStatus = this._id('index-status'); + var reindexButton = this._id('reindex'); + + var status = Zotero.Fulltext.getIndexedState(this.item.id); + var str = 'fulltext.indexState.'; + switch (status) { + case Zotero.Fulltext.INDEX_STATE_UNAVAILABLE: + str += 'unavailable'; + break; + case Zotero.Fulltext.INDEX_STATE_UNINDEXED: + str = 'general.no'; + break; + case Zotero.Fulltext.INDEX_STATE_PARTIAL: + str += 'partial'; + break; + case Zotero.Fulltext.INDEX_STATE_INDEXED: + str = 'general.yes'; + break; + } + str = Zotero.getString('fulltext.indexState.indexed') + ': ' + + Zotero.getString(str); + indexStatus.setAttribute('value', str); + + // Reindex button tooltip (string stored in zotero.properties) + var str = Zotero.getString('pane.items.menu.reindexItem'); + reindexButton.setAttribute('tooltiptext', str); + + if (this.editable && + Zotero.Fulltext.pdfConverterIsRegistered() && + Zotero.Fulltext.canReindex(this.item.id)) { + reindexButton.setAttribute('hidden', false); + } + else { + reindexButton.setAttribute('hidden', true); + } + ]]> + </body> + </method> + + + <method name="_id"> + <parameter name="id"/> + <body> + <![CDATA[ + return document.getAnonymousNodes(this)[0].getElementsByAttribute('id', id)[0]; + ]]> + </body> + </method> + </implementation> + + <content> + <vbox id="attachment-box" flex="1" orient="vertical" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <label id="title"/> + <hbox id="go-buttons"> + <button id="view" flex="1"/> + <button id="show" label="&zotero.item.attachment.file.show;" flex="1"/> + </hbox> + <label id="url" crop="end"/> + + <label id="accessed"/> + <label id="pages"/> + + <hbox id="index-box"> + <label id="index-status"/> + <toolbarbutton id="reindex" oncommand="ZoteroPane.reindexItem()"/> + </hbox> + + <zoteronoteeditor id="note-editor" notitle="1" flex="1"/> + + <button id="select-button" hidden="true"/> + </vbox> + </content> + </binding> +</bindings> diff --git a/chrome/content/zotero/bindings/merge.xml b/chrome/content/zotero/bindings/merge.xml @@ -62,6 +62,11 @@ return; } + // Check for note or attachment + if (this.type == 'item') { + this.type = this._getTypeFromItem(val); + } + // Clone object so changes in merge pane don't affect it this._leftpane.ref = val.clone(true); this._leftpane.original = val; @@ -83,6 +88,11 @@ else { // TODO: Make sure object is the correct type + // Check for note or attachment if not already set + if (this._leftpane.ref == 'deleted' && this.type == 'item') { + this.type = this._getTypeFromItem(val); + } + // Clone object so changes in merge pane don't affect it this._rightpane.ref = val.clone(true); this._rightpane.original = val; @@ -208,6 +218,25 @@ </body> </method> + <method name="_getTypeFromItem"> + <parameter name="obj"/> + <body> + <![CDATA[ + if (!(obj instanceof Zotero.Item)) { + throw ("obj is not a Zotero.Item in merge.xml"); + } + if (obj.isAttachment()) { + return 'attachment'; + } + else if (obj.isNote()) { + return 'note'; + } + else { + return 'item'; + } + ]]> + </body> + </method> <method name="_id"> <parameter name="id"/> @@ -281,6 +310,10 @@ elementName = 'zoteroitembox'; break; + case 'attachment': + elementName = 'zoteroattachmentbox'; + break; + case 'note': elementName = 'zoteronoteeditor'; break; @@ -314,6 +347,7 @@ // Type-specific settings switch (this.type) { + case 'attachment': case 'note': objbox.buttonCaption = 'Choose this version'; break; diff --git a/chrome/content/zotero/bindings/noteeditor.xml b/chrome/content/zotero/bindings/noteeditor.xml @@ -40,13 +40,12 @@ <field name="displayRelated">false</field> <field name="displayButton">false</field> + <field name="buttonCaption"/> <field name="parentClickHandler"/> <field name="keyDownHandler"/> <field name="commandHandler"/> <field name="clickHandler"/> - <field name="buttonCaption"/> - <!-- Modes are predefined settings groups for particular tasks --> <field name="_mode">"view"</field> <property name="mode" onget="return this._mode;"> @@ -78,7 +77,9 @@ break; case 'mergeedit': - this.editable = true; + // Not currently implemented + // (editing works, but value isn't saved) + //this.editable = true; this.keyDownHandler = this.handleKeyDown; break; @@ -146,7 +147,6 @@ var parentbox = this._id('citeLabel'); var textbox = this._id('noteField'); var button = this._id('goButton'); - var button = this._id('goButton'); if (this.editable) { textbox.removeAttribute('readonly'); diff --git a/chrome/content/zotero/bindings/styled-textbox.xml b/chrome/content/zotero/bindings/styled-textbox.xml @@ -34,37 +34,45 @@ <field name="_loadHandler"/> <field name="_commandString"/> <field name="_eventHandler"/> + <field name="_editor"/> + <field name="_value"/> <field name="_timer"/> <field name="_focus"/> + <field name="_constructed"/> + <field name="_loadOnConstruct"/> <constructor><![CDATA[ - this.mode = this.getAttribute('mode'); - - this._iframe = document.getAnonymousElementByAttribute(this, "anonid", "rt-view"); - this._editor = null; - this._value = null; - - this._rtfMap = { - "\\":"\\\\", - "<em>":"\\i ", - "</em>":"\\i0 ", - "<i>":"\\i ", - "</i>":"\\i0 ", - "<strong>":"\\b ", - "</strong>":"\\b0 ", - "<b>":"\\b ", - "</b>":"\\b0 ", - "<u>":"\\ul ", - "</u>":"\\ul0 ", - "<br />":"\x0B", - "<sup>":"\\super ", - "</sup>":"\\super0 ", - "<sub>":"\\sub ", - "</sub>":"\\sub0 ", - // there's no way to mimic a tab stop in CSS without - // tables, which wouldn't work here. - '<span class="tab">&nbsp;</span>':"\t" - }; + this.mode = this.getAttribute('mode'); + + this._iframe = document.getAnonymousElementByAttribute(this, "anonid", "rt-view"); + + this._rtfMap = { + "\\":"\\\\", + "<em>":"\\i ", + "</em>":"\\i0 ", + "<i>":"\\i ", + "</i>":"\\i0 ", + "<strong>":"\\b ", + "</strong>":"\\b0 ", + "<b>":"\\b ", + "</b>":"\\b0 ", + "<u>":"\\ul ", + "</u>":"\\ul0 ", + "<br />":"\x0B", + "<sup>":"\\super ", + "</sup>":"\\super0 ", + "<sub>":"\\sub ", + "</sub>":"\\sub0 ", + // there's no way to mimic a tab stop in CSS without + // tables, which wouldn't work here. + '<span class="tab">&nbsp;</span>':"\t" + }; + + this._constructed = true; + + if (this._loadOnConstruct) { + this._load(); + } ]]></constructor> <property name="mode"> @@ -178,7 +186,11 @@ Zotero.debug('No editor yet'); this._value = val; - if (!this._loaded) { + if (!this._constructed) { + Zotero.debug('Styled textbox not yet constructed', 2); + this._loadOnConstruct = true; + } + else if (!this._loaded) { this._load(); } return ; diff --git a/chrome/content/zotero/merge.js b/chrome/content/zotero/merge.js @@ -42,13 +42,6 @@ var Zotero_Merge_Window = new function () { switch (_mergeGroup.type) { case 'item': - var firstObj = _objects[0][0] == 'deleted' ? _objects[0][1] : _objects[0][0]; - if (firstObj.isNote()) { - _mergeGroup.type = 'note'; - } - else { - _mergeGroup.type = 'item'; - } break; default: @@ -122,14 +115,23 @@ var Zotero_Merge_Window = new function () { // Adjust counter _numObjects.value = _pos + 1; - _mergeGroup.left = _objects[_pos][0]; - _mergeGroup.right = _objects[_pos][1]; - - // Restore previously merged object into merge pane - if (_merged[_pos]) { - _mergeGroup.merge = _merged[_pos].ref; - _mergeGroup.leftpane.removeAttribute("selected"); - _mergeGroup.rightpane.removeAttribute("selected"); + try { + _mergeGroup.left = _objects[_pos][0]; + _mergeGroup.right = _objects[_pos][1]; + + // Restore previously merged object into merge pane + if (_merged[_pos]) { + _mergeGroup.merge = _merged[_pos].ref; + _mergeGroup.leftpane.removeAttribute("selected"); + _mergeGroup.rightpane.removeAttribute("selected"); + } + } + catch (e) { + var prompt = Components.classes["@mozilla.org/network/default-prompt;1"] + .createInstance(Components.interfaces.nsIPrompt); + prompt.alert(Zotero.getString('general.error'), e); + _wizard.getButton('cancel').click(); + return false; } if (_mergeGroup.type == 'item') { diff --git a/chrome/content/zotero/overlay.js b/chrome/content/zotero/overlay.js @@ -50,7 +50,6 @@ var ZoteroPane = new function() this.updateTagFilter = updateTagFilter; this.onCollectionSelected = onCollectionSelected; this.itemSelected = itemSelected; - this.updateItemIndexedState = updateItemIndexedState; this.reindexItem = reindexItem; this.duplicateSelectedItem = duplicateSelectedItem; this.deleteSelectedItem = deleteSelectedItem; @@ -83,7 +82,6 @@ var ZoteroPane = new function() this.addAttachmentFromPage = addAttachmentFromPage; this.viewAttachment = viewAttachment; this.viewSelectedAttachment = viewSelectedAttachment; - this.showSelectedAttachmentInFilesystem = showSelectedAttachmentInFilesystem; this.showAttachmentNotFoundDialog = showAttachmentNotFoundDialog; this.relinkAttachment = relinkAttachment; this.reportErrors = reportErrors; @@ -797,12 +795,7 @@ var ZoteroPane = new function() if(item.ref.isNote()) { var noteEditor = document.getElementById('zotero-note-editor'); - if (this.itemsView.readOnly) { - noteEditor.mode = 'view'; - } - else { - noteEditor.mode = 'edit'; - } + noteEditor.mode = this.itemsView.readOnly ? 'view' : 'edit'; // If loading new or different note, disable undo while we repopulate the text field // so Undo doesn't end up clearing the field. This also ensures that Undo doesn't @@ -828,165 +821,9 @@ var ZoteroPane = new function() } else if(item.ref.isAttachment()) { - // DEBUG: this is annoying -- we really want to use an abstracted - // version of createValueElement() from itemPane.js - // (ideally in an XBL binding) - - // Wrap title to multiple lines if necessary - var label = document.getElementById('zotero-attachment-label'); - while (label.hasChildNodes()) - { - label.removeChild(label.firstChild); - } - var val = item.getField('title'); - - var firstSpace = val.indexOf(" "); - // Crop long uninterrupted text - if ((firstSpace == -1 && val.length > 29 ) || firstSpace > 29) - { - label.setAttribute('crop', 'end'); - label.setAttribute('value', val); - } - // Create a <description> element, essentially - else - { - label.removeAttribute('value'); - label.appendChild(document.createTextNode(val)); - } - - // For the time being, use a silly little popup - label.className = 'zotero-clicky'; - label.onclick = function(event){ - var nsIPS = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] - .getService(Components.interfaces.nsIPromptService); - - var newTitle = { value: val }; - var checkState = { value: Zotero.Prefs.get('lastRenameAssociatedFile') }; - - while (true) { - // Don't show "Rename associated file" option for - // linked URLs - if (item.ref.getAttachmentLinkMode() == - Zotero.Attachments.LINK_MODE_LINKED_URL) { - var result = nsIPS.prompt( - window, - '', - Zotero.getString('pane.item.attachments.rename.title'), - newTitle, - null, - {} - ); - - // If they hit cancel or left it blank - if (!result || !newTitle.value) { - return; - } - - break; - } - - var result = nsIPS.prompt( - window, - '', - Zotero.getString('pane.item.attachments.rename.title'), - newTitle, - Zotero.getString('pane.item.attachments.rename.renameAssociatedFile'), - checkState - ); - - // If they hit cancel or left it blank - if (!result || !newTitle.value) { - return; - } - - Zotero.Prefs.set('lastRenameAssociatedFile', checkState.value); - - // Rename associated file - if (checkState.value) { - var renamed = item.ref.renameAttachmentFile(newTitle.value); - if (renamed == -1) { - var confirmed = confirm(newTitle.value + ' exists. Overwrite existing file?'); - if (confirmed) { - item.ref.renameAttachmentFile(newTitle.value, true); - break; - } - // If they said not to overwrite existing file, - // start again - continue; - } - else if (renamed == -2 || !renamed) { - alert(Zotero.getString('pane.item.attachments.rename.error')); - return; - } - } - - break; - } - - if (newTitle.value != val) { - item.ref.setField('title', newTitle.value); - item.ref.save(); - } - } - - - var isImportedURL = item.ref.getAttachmentLinkMode() == Zotero.Attachments.LINK_MODE_IMPORTED_URL; - - // Metadata for URL's - if (item.ref.getAttachmentLinkMode() == Zotero.Attachments.LINK_MODE_LINKED_URL - || isImportedURL) - { - // "View Page"/"View Snapshot" label - if (isImportedURL) - { - var str = Zotero.getString('pane.item.attachments.view.snapshot'); - } - else - { - var str = Zotero.getString('pane.item.attachments.view.link'); - } - - document.getElementById('zotero-attachment-show').setAttribute('hidden', !isImportedURL); - - // URL - document.getElementById('zotero-attachment-url').setAttribute('value', item.getField('url')); - document.getElementById('zotero-attachment-url').setAttribute('hidden', false); - // Access date - document.getElementById('zotero-attachment-accessed').setAttribute('value', - Zotero.getString('itemFields.accessDate') + ': ' - + Zotero.Date.sqlToDate(item.getField('accessDate'), true).toLocaleString()); - document.getElementById('zotero-attachment-accessed').setAttribute('hidden', false); - } - // Metadata for files - else - { - var str = Zotero.getString('pane.item.attachments.view.file'); - document.getElementById('zotero-attachment-show').setAttribute('hidden', false); - document.getElementById('zotero-attachment-url').setAttribute('hidden', true); - document.getElementById('zotero-attachment-accessed').setAttribute('hidden', true); - } - - document.getElementById('zotero-attachment-view').setAttribute('label', str); - - // Display page count - var pages = Zotero.Fulltext.getPages(item.ref.id); - var pages = pages ? pages.total : null; - var pagesRow = document.getElementById('zotero-attachment-pages'); - if (pages) { - var str = Zotero.getString('itemFields.pages') + ': ' + pages; - pagesRow.setAttribute('value', str); - pagesRow.setAttribute('hidden', false); - } - else { - pagesRow.setAttribute('hidden', true); - } - - this.updateItemIndexedState(); - - var noteEditor = document.getElementById('zotero-attachment-note-editor'); - noteEditor.mode = 'edit'; - noteEditor.parent = null; - noteEditor.item = item.ref; + var attachmentBox = document.getElementById('zotero-attachment-box'); + attachmentBox.mode = this.itemsView.readOnly ? 'view' : 'edit'; + attachmentBox.item = item.ref; document.getElementById('zotero-item-pane-content').selectedIndex = 3; } @@ -1015,48 +852,6 @@ var ZoteroPane = new function() } - /* - * Update Indexed: (Yes|No|Partial) line in attachment info pane - */ - function updateItemIndexedState() { - var indexBox = document.getElementById('zotero-attachment-index-box'); - var indexStatus = document.getElementById('zotero-attachment-index-status'); - var reindexButton = document.getElementById('zotero-attachment-reindex'); - - var item = this.itemsView._getItemAtRow(this.itemsView.selection.currentIndex); - var status = Zotero.Fulltext.getIndexedState(item.ref.id); - var str = 'fulltext.indexState.'; - switch (status) { - case Zotero.Fulltext.INDEX_STATE_UNAVAILABLE: - str += 'unavailable'; - break; - case Zotero.Fulltext.INDEX_STATE_UNINDEXED: - str = 'general.no'; - break; - case Zotero.Fulltext.INDEX_STATE_PARTIAL: - str += 'partial'; - break; - case Zotero.Fulltext.INDEX_STATE_INDEXED: - str = 'general.yes'; - break; - } - str = Zotero.getString('fulltext.indexState.indexed') + ': ' + - Zotero.getString(str); - indexStatus.setAttribute('value', str); - - // Reindex button tooltip (string stored in zotero.properties) - var str = Zotero.getString('pane.items.menu.reindexItem'); - reindexButton.setAttribute('tooltiptext', str); - - if (Zotero.Fulltext.canReindex(item.ref.id)) { - reindexButton.setAttribute('hidden', false); - } - else { - reindexButton.setAttribute('hidden', true); - } - } - - function reindexItem() { var items = this.getSelectedItems(); if (!items) { @@ -1070,7 +865,7 @@ var ZoteroPane = new function() var itemID = items[i].id; Zotero.Fulltext.indexItems(itemID, true); } - this.updateItemIndexedState(); + document.getElementById('zotero-attachment-box').updateItemIndexedState(); } @@ -2100,13 +1895,13 @@ var ZoteroPane = new function() } - function viewAttachment(itemID, event) { + function viewAttachment(itemID, event, noLocateOnMissing) { var attachment = Zotero.Items.get(itemID); if (!attachment.isAttachment()) { throw ("Item " + itemID + " is not an attachment in ZoteroPane.viewAttachment()"); } - if (attachment.getAttachmentLinkMode() == Zotero.Attachments.LINK_MODE_LINKED_URL) { + if (attachment.attachmentLinkMode == Zotero.Attachments.LINK_MODE_LINKED_URL) { this.loadURI(attachment.getField('url'), event); return; } @@ -2146,61 +1941,67 @@ var ZoteroPane = new function() } } else { - this.showAttachmentNotFoundDialog(itemID); + this.showAttachmentNotFoundDialog(itemID, noLocateOnMissing); } } - function viewSelectedAttachment(event) + function viewSelectedAttachment(event, noLocateOnMissing) { if (this.itemsView && this.itemsView.selection.count == 1) { - this.viewAttachment(this.getSelectedItems(true)[0], event); + this.viewAttachment(this.getSelectedItems(true)[0], event, noLocateOnMissing); } } - function showSelectedAttachmentInFilesystem() - { - if (this.itemsView && this.itemsView.selection.count == 1) { - var attachment = this.getSelectedItems()[0]; - - if (attachment.getAttachmentLinkMode() != Zotero.Attachments.LINK_MODE_LINKED_URL) - { - var file = attachment.getFile(); - if (file){ + this.showAttachmentInFilesystem = function (itemID, noLocateOnMissing) { + var attachment = Zotero.Items.get(itemID) + if (attachment.attachmentLinkMode != Zotero.Attachments.LINK_MODE_LINKED_URL) { + var file = attachment.getFile(); + if (file){ + try { + file.reveal(); + } + catch (e) { + // On platforms that don't support nsILocalFile.reveal() (e.g. Linux), + // "double-click" the parent directory try { - file.reveal(); + var parent = file.parent.QueryInterface(Components.interfaces.nsILocalFile); + parent.launch(); } + // If launch also fails, try the OS handler catch (e) { - // On platforms that don't support nsILocalFile.reveal() (e.g. Linux), - // "double-click" the parent directory - try { - var parent = file.parent.QueryInterface(Components.interfaces.nsILocalFile); - parent.launch(); - } - // If launch also fails, try the OS handler - catch (e) { - var uri = Components.classes["@mozilla.org/network/io-service;1"]. - getService(Components.interfaces.nsIIOService). - newFileURI(parent); - var protocolService = - Components.classes["@mozilla.org/uriloader/external-protocol-service;1"]. - getService(Components.interfaces.nsIExternalProtocolService); - protocolService.loadUrl(uri); - } + var uri = Components.classes["@mozilla.org/network/io-service;1"]. + getService(Components.interfaces.nsIIOService). + newFileURI(parent); + var protocolService = + Components.classes["@mozilla.org/uriloader/external-protocol-service;1"]. + getService(Components.interfaces.nsIExternalProtocolService); + protocolService.loadUrl(uri); } } - else { - this.showAttachmentNotFoundDialog(attachment.id) - } + } + else { + this.showAttachmentNotFoundDialog(attachment.id, noLocateOnMissing) } } } - function showAttachmentNotFoundDialog(itemID) { + function showAttachmentNotFoundDialog(itemID, noLocate) { var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]. createInstance(Components.interfaces.nsIPromptService); + + + // Don't show Locate button + if (noLocate) { + var index = ps.alert(null, + Zotero.getString('pane.item.attachments.fileNotFound.title'), + Zotero.getString('pane.item.attachments.fileNotFound.text') + ); + return; + } + var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING) + (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL); var index = ps.confirmEx(null, diff --git a/chrome/content/zotero/overlay.xul b/chrome/content/zotero/overlay.xul @@ -379,25 +379,7 @@ <button id="zotero-view-note-button" label="&zotero.notes.separate;" oncommand="ZoteroPane.openNoteWindow(this.getAttribute('noteID')); if(this.hasAttribute('sourceID')) ZoteroPane.selectItem(this.getAttribute('sourceID'));"/> </vbox> <!-- Attachment info pane --> - <vbox id="zotero-view-attachment" flex="1"> - <label id="zotero-attachment-label"/> - <hbox> - <button id="zotero-attachment-view" flex="1" oncommand="ZoteroPane.viewSelectedAttachment(event);"/> - <button id="zotero-attachment-show" label="&zotero.item.attachment.file.show;" flex="1" oncommand="ZoteroPane.showSelectedAttachmentInFilesystem()"/> - </hbox> - <label id="zotero-attachment-url" class="text-link" crop="end" onclick="ZoteroPane.loadURI(this.value, event)"/> - - - <label id="zotero-attachment-accessed"/> - <label id="zotero-attachment-pages"/> - - <hbox id="zotero-attachment-index-box"> - <label id="zotero-attachment-index-status"/> - <toolbarbutton id="zotero-attachment-reindex" oncommand="ZoteroPane.reindexItem()"/> - </hbox> - - <zoteronoteeditor id="zotero-attachment-note-editor" notitle="1" flex="1"/> - </vbox> + <zoteroattachmentbox id="zotero-attachment-box" flex="1"/> </deck> </groupbox> </vbox> diff --git a/chrome/content/zotero/xpcom/data/item.js b/chrome/content/zotero/xpcom/data/item.js @@ -2949,21 +2949,51 @@ Zotero.Item.prototype.diff = function (item, includeMatches, ignoreOnlyDateModif var diff = []; var thisData = this.serialize(); var otherData = item.serialize(); + var numDiffs = Zotero.Items.diff(thisData, otherData, diff, includeMatches); diff[0].creators = []; diff[1].creators = []; - // TODO: creators - - // TODO: attachments + // TODO: creators? + // TODO: tags? + // TODO: related? + // TODO: annotations - // TODO: notes + var changed = false; - // TODO: tags + if (thisData.attachment) { + for (var field in thisData.attachment) { + changed = thisData.attachment[field] != otherData.attachment[field]; + if (includeMatches || changed) { + if (!diff[0].attachment) { + diff[0].attachment = {}; + diff[1].attachment = {}; + } + diff[0].attachment[field] = thisData.attachment[field]; + diff[1].attachment[field] = otherData.attachment[field]; + } + + if (changed) { + numDiffs++; + } + } + } - // TODO: related + if (thisData.note != undefined) { + changed = thisData.note != otherData.note; + if (includeMatches || changed) { + diff[0].note = thisData.note; + diff[1].note = otherData.note; + } + + if (changed) { + numDiffs++; + } + } - // TODO: annotations + //Zotero.debug(thisData); + //Zotero.debug(otherData); + //Zotero.debug(diff); // DEBUG: ignoreOnlyDateModified wouldn't work if includeMatches was set? if (numDiffs == 0 || @@ -2980,10 +3010,6 @@ Zotero.Item.prototype.diff = function (item, includeMatches, ignoreOnlyDateModif * Returns an unsaved copy of the item */ Zotero.Item.prototype.clone = function(includePrimary) { - if (this.isAttachment()) { - throw ('Cloning attachment items not supported in Zotero.Item.clone()'); - } - Zotero.debug('Cloning item ' + this.id); Zotero.DB.beginTransaction(); @@ -3004,23 +3030,15 @@ Zotero.Item.prototype.clone = function(includePrimary) { } } - // Note - if (this.isNote()) { - newItem.setNote(this.getNote()); - var parent = this.getSource(); - if (parent) { - newItem.setSource(parent); + for (var field in obj.fields) { + var fieldID = Zotero.ItemFields.getID(field); + if (fieldID && Zotero.ItemFields.isValidForType(fieldID, itemTypeID)) { + newItem.setField(field, obj.fields[field]); } } + // Regular item - else { - for (var field in obj.fields) { - var fieldID = Zotero.ItemFields.getID(field); - if (fieldID && Zotero.ItemFields.isValidForType(fieldID, itemTypeID)) { - newItem.setField(field, obj.fields[field]); - } - } - + if (this.isRegularItem()) { if (includePrimary) { // newItem = loaded from db // obj = in-memory @@ -3054,6 +3072,21 @@ Zotero.Item.prototype.clone = function(includePrimary) { } } } + else { + newItem.setNote(this.getNote()); + var parent = this.getSource(); + if (parent) { + newItem.setSource(parent); + } + + if (this.isAttachment()) { + newItem.attachmentLinkMode = this.attachmentLinkMode; + newItem.attachmentMIMEType = this.attachmentMIMEType; + newItem.attachmentCharset = this.attachmentCharset; + newItem.attachmentPath = this.attachmentPath; + newItem.attachmentSyncState = this.attachmentSyncState; + } + } if (obj.tags) { for each(var tag in obj.tags) { diff --git a/chrome/content/zotero/xpcom/sync.js b/chrome/content/zotero/xpcom/sync.js @@ -1626,11 +1626,6 @@ Zotero.Sync.Server.Data = new function() { continue; } } - if (obj.isAttachment()) { - var msg = "Reconciliation unimplemented for attachment items"; - alert(msg); - throw(msg); - } reconcile = true; break; @@ -1894,7 +1889,6 @@ Zotero.Sync.Server.Data = new function() { default: alert('Delete reconciliation unimplemented for ' + types); throw ('Delete reconciliation unimplemented for ' + types); - } } } diff --git a/chrome/skin/default/zotero/bindings/attachmentbox.css b/chrome/skin/default/zotero/bindings/attachmentbox.css @@ -0,0 +1,36 @@ +/* Don't collapse blank attachment titles, since it prevents renaming */ +#title +{ + min-height: 1.25em; +} + +#view, #show +{ + list-style-image: url('chrome://zotero/skin/toolbar-go-arrow.png'); + -moz-box-direction: reverse; + -moz-box-flex: 1; +} + +#index-box +{ + -moz-box-align: center; +} + +#reindex +{ + margin: 0; + padding: 0; + list-style-image: url(chrome://zotero/skin/arrow_refresh.png); +} + +#reindex .toolbarbutton-icon +{ + margin: 0; + padding: 0; +} + +#index-box > button +{ + font-size: .95em; + padding: 0; +} diff --git a/chrome/skin/default/zotero/overlay.css b/chrome/skin/default/zotero/overlay.css @@ -274,58 +274,12 @@ list-style-image: url('chrome://zotero/skin/search-cancel-active.png'); } -#zotero-attachment-view, #zotero-attachment-show -{ - list-style-image: url('chrome://zotero/skin/toolbar-go-arrow.png'); - -moz-box-direction: reverse; - -moz-box-flex: 1; -} - - -/* Reindex button in attachment pane */ -#zotero-attachment-index-box -{ - -moz-box-align: center; -} -#zotero-attachment-reindex -{ - margin: 0; - padding: 0; - list-style-image: url(chrome://zotero/skin/arrow_refresh.png); -} - - -#zotero-attachment-reindex .toolbarbutton-icon -{ - margin: 0; - padding: 0; -} - - #zotero-view-item > vbox { overflow: auto; margin-left: 5px; } -/* Don't collapse blank attachment labels, since it prevents renaming */ -#zotero-attachment-label -{ - min-height: 1.25em; -} - -#zotero-attachment-index-box -{ - -moz-box-align: center; -} - -#zotero-attachment-index-box > button -{ - font-size: .95em; - padding: 0; -} - - #zotero-splitter { border-top: none; diff --git a/chrome/skin/default/zotero/zotero.css b/chrome/skin/default/zotero/zotero.css @@ -68,6 +68,12 @@ textbox[type="styled"] -moz-binding: url('chrome://zotero/content/bindings/styled-textbox.xml#styled-textbox'); } +zoteroattachmentbox +{ + -moz-binding: url('chrome://zotero/content/bindings/attachmentbox.xml#attachment-box'); +} + + zoteronoteeditor { -moz-binding: url('chrome://zotero/content/bindings/noteeditor.xml#note-editor');