commit e417d8e690b33be6cae789abd02df28abf42a4cb
parent f635ee7788f5fdff89a79d54f5f77c3c4d2be5c7
Author: David Norton <david@nortoncrew.com>
Date: Thu, 15 Jun 2006 22:35:48 +0000
[interface] Although commented out, code in place to accept URL drags into collections (waiting on an Ingester.scrapeURL function)
[interface] Multi-notes functionality (waiting on data layer)
[docs] Major internal documentation written for itemTreeView.js and collectionTreeView.js (this actually does work ;-))
Diffstat:
4 files changed, 324 insertions(+), 52 deletions(-)
diff --git a/chrome/chromeFiles/content/scholar/collectionTreeView.js b/chrome/chromeFiles/content/scholar/collectionTreeView.js
@@ -1,3 +1,14 @@
+////////////////////////////////////////////////////////////////////////////////
+///
+/// CollectionTreeView
+/// -- handles the link between an individual tree and the data layer
+/// -- displays only collections, in a hierarchy (no items)
+///
+////////////////////////////////////////////////////////////////////////////////
+
+/*
+ * Constructor the the CollectionTreeView object
+ */
Scholar.CollectionTreeView = function()
{
this._treebox = null;
@@ -6,6 +17,9 @@ Scholar.CollectionTreeView = function()
this._unregisterID = Scholar.Notifier.registerColumnTree(this);
}
+/*
+ * Called by the tree itself
+ */
Scholar.CollectionTreeView.prototype.setTree = function(treebox)
{
if(this._treebox)
@@ -13,6 +27,10 @@ Scholar.CollectionTreeView.prototype.setTree = function(treebox)
this._treebox = treebox;
}
+/*
+ * Reload the rows from the data access methods
+ * (doesn't call the tree.invalidate methods, etc.)
+ */
Scholar.CollectionTreeView.prototype.refresh = function()
{
this._dataItems = new Array();
@@ -27,7 +45,7 @@ Scholar.CollectionTreeView.prototype.refresh = function()
}
/*
- * Is called by Scholar.Notifier on any changes to the data layer
+ * Called by Scholar.Notifier on any changes to collections in the data layer
*/
Scholar.CollectionTreeView.prototype.notify = function(action, type, ids)
{
@@ -102,13 +120,20 @@ Scholar.CollectionTreeView.prototype.notify = function(action, type, ids)
}
/*
- * Unregisters itself from Scholar.Notifier (called on window close)
+ * Unregisters view from Scholar.Notifier (called on window close)
*/
Scholar.CollectionTreeView.prototype.unregister = function()
{
Scholar.Notifier.unregisterColumnTree(this._unregisterID);
}
+////////////////////////////////////////////////////////////////////////////////
+///
+/// nsITreeView functions
+/// http://www.xulplanet.com/references/xpcomref/ifaces/nsITreeView.html
+///
+////////////////////////////////////////////////////////////////////////////////
+
Scholar.CollectionTreeView.prototype.getCellText = function(row, column)
{
var obj = this._getItemAtRow(row);
@@ -137,6 +162,8 @@ Scholar.CollectionTreeView.prototype.isContainerOpen = function(row)
Scholar.CollectionTreeView.prototype.isContainerEmpty = function(row)
{
+ //NOTE: this returns true if the collection has no child collections
+
var itemGroup = this._getItemAtRow(row);
if(itemGroup.isCollection())
return !itemGroup.ref.hasChildCollections();
@@ -170,6 +197,9 @@ Scholar.CollectionTreeView.prototype.hasNextSibling = function(row, afterIndex)
}
}
+/*
+ * Opens/closes the specified row
+ */
Scholar.CollectionTreeView.prototype.toggleOpenState = function(row)
{
var count = 0; //used to tell the tree how many rows were added/removed
@@ -202,6 +232,15 @@ Scholar.CollectionTreeView.prototype.toggleOpenState = function(row)
this._refreshHashMap();
}
+////////////////////////////////////////////////////////////////////////////////
+///
+/// Additional functions for managing data in the tree
+///
+////////////////////////////////////////////////////////////////////////////////
+
+/*
+ * Delete the selection
+ */
Scholar.CollectionTreeView.prototype.deleteSelection = function()
{
if(this.selection.count == 0)
@@ -240,23 +279,37 @@ Scholar.CollectionTreeView.prototype.deleteSelection = function()
this.selection.select(this.rowCount-1);
}
-Scholar.CollectionTreeView.prototype._showItem = function(item, level, beforeRow)
+/*
+ * Called by various view functions to show a row
+ *
+ * itemGroup: reference to the ItemGroup
+ * level: the indent level of the row
+ * beforeRow: row index to insert new row before
+ */
+Scholar.CollectionTreeView.prototype._showItem = function(itemGroup, level, beforeRow)
{
- this._dataItems.splice(beforeRow, 0, [item, false, level]); this.rowCount++;
+ this._dataItems.splice(beforeRow, 0, [itemGroup, false, level]); this.rowCount++;
}
+/*
+ * Called by view to hide specified row
+ */
Scholar.CollectionTreeView.prototype._hideItem = function(row)
{
this._dataItems.splice(row,1); this.rowCount--;
}
+/*
+ * Returns a reference to the collection at row (see Scholar.Collection in data_access.js)
+ */
Scholar.CollectionTreeView.prototype._getItemAtRow = function(row)
{
return this._dataItems[row][0];
}
/*
- * Create hash map of collection ids to row indexes
+ * Creates hash map of collection ids to row indexes
+ * e.g., var rowForID = this._collectionRowMap[]
*/
Scholar.CollectionTreeView.prototype._refreshHashMap = function()
{
@@ -268,8 +321,17 @@ Scholar.CollectionTreeView.prototype._refreshHashMap = function()
}
-/* DRAG AND DROP FUNCTIONS */
+////////////////////////////////////////////////////////////////////////////////
+///
+/// Drag-and-drop functions:
+/// canDrop() and drop() are for nsITreeView
+/// onDragStart(), getSupportedFlavours(), and onDrop() for nsDragAndDrop.js + nsTransferable.js
+///
+////////////////////////////////////////////////////////////////////////////////
+/*
+ * Called while a drag is over the tree.
+ */
Scholar.CollectionTreeView.prototype.canDrop = function(row, orient)
{
if(typeof row == 'object') //workaround... two different services call canDrop (nsDragAndDrop, and the tree)
@@ -288,23 +350,28 @@ Scholar.CollectionTreeView.prototype.canDrop = function(row, orient)
}
var data = dataSet.first.first;
var dataType = data.flavour.contentType;
- var rowCollection = this._getItemAtRow(row).ref;
- //Check to make sure that the highlighting is done right
- if(orient == 1 && row == 0 && dataType == 'scholar/collection')
+
+ //Highlight the rows correctly on drag:
+ if(orient == 1 && row == 0 && dataType == 'scholar/collection') //for dropping collections into root level
{
return true;
}
- else if(orient == 0)
+ else if(orient == 0) //directly on a row...
{
+ var rowCollection = this._getItemAtRow(row).ref; //the collection we are dragging over
+
if(dataType == 'scholar/item' || dataType == "text/x-moz-url")
- return true;
+ return true; //items can be dropped on anything
else if(dataType='scholar/collection' && data.data != rowCollection.getID() && !Scholar.Collections.get(data.data).hasDescendent('collection',rowCollection.getID()) )
- return true;
+ return true; //collections cannot be dropped on themselves, nor in their children
}
return false;
}
+/*
+ * Called when something's been dropped on or next to a row
+ */
Scholar.CollectionTreeView.prototype.drop = function(row, orient)
{
var dataSet = nsTransferable.get(this.getSupportedFlavours(),nsDragAndDrop.getDragData, true);
@@ -340,19 +407,31 @@ Scholar.CollectionTreeView.prototype.drop = function(row, orient)
}
else if(dataType == 'text/x-moz-url' && this.canDrop(row, orient))
{
- alert(data.data);
- var targetCollection = this._getItemAtRow(row).ref;
- /*for(var i = 0; i<ids.length; i++)
- targetCollection.addItem(ids[i]); */
+ var url = data.data.split("\n")[0];
+
+ /* WAITING FOR INGESTER SUPPORT
+ var newItem = Scholar.Ingester.scrapeURL(url);
+
+ if(newItem)
+ this._getItemAtRow(row).ref.addItem(newItem.getID());
+ */
}
}
+/*
+ * Begin a drag
+ */
Scholar.CollectionTreeView.prototype.onDragStart = function(evt,transferData,action)
{
transferData.data=new TransferData();
+
+ //attach ID
transferData.data.addDataForFlavour("scholar/collection",this._getItemAtRow(this.selection.currentIndex).ref.getID());
}
+/*
+ * Returns the supported drag flavors
+ */
Scholar.CollectionTreeView.prototype.getSupportedFlavours = function ()
{
var flavors = new FlavourSet();
@@ -362,9 +441,16 @@ Scholar.CollectionTreeView.prototype.getSupportedFlavours = function ()
return flavors;
}
+/*
+ * Called by nsDragAndDrop.js for any sort of drop on the tree
+ */
Scholar.CollectionTreeView.prototype.onDrop = function (evt,dropdata,session) { }
-/* MORE TREEVIEW FUNCTIONS THAT HAVE TO BE HERE */
+////////////////////////////////////////////////////////////////////////////////
+///
+/// Functions for nsITreeView that we have to stub out.
+///
+////////////////////////////////////////////////////////////////////////////////
Scholar.CollectionTreeView.prototype.isSorted = function() { return false; }
Scholar.CollectionTreeView.prototype.isSeparator = function(row) { return false; }
@@ -377,9 +463,12 @@ Scholar.CollectionTreeView.prototype.performActionOnCell = function(action, row,
Scholar.CollectionTreeView.prototype.getProgressMode = function(row, col) { }
Scholar.CollectionTreeView.prototype.cycleHeader = function(column) { }
-//
-// SCHOLAR ITEMGROUP
-//
+////////////////////////////////////////////////////////////////////////////////
+///
+/// Scholar ItemGroup -- a sort of "super class" for Collection, library,
+/// and eventually smartSearch
+///
+////////////////////////////////////////////////////////////////////////////////
Scholar.ItemGroup = function(type, ref)
{
diff --git a/chrome/chromeFiles/content/scholar/itemPane.js b/chrome/chromeFiles/content/scholar/itemPane.js
@@ -3,7 +3,9 @@ ScholarItemPane = new function()
var _dynamicFields;
var _creatorTypeMenu;
var _beforeRow;
- var _notesPane;
+ var _notesMenu;
+ var _notesField;
+ var _notesLabel;
var _creatorCount;
@@ -17,12 +19,18 @@ ScholarItemPane = new function()
this.hideEditor = hideEditor;
this.modifyField = modifyField;
this.modifyCreator = modifyCreator;
+ this.modifySelectedNote = modifySelectedNote;
+ this.removeSelectedNote = removeSelectedNote;
+ this.addNote = addNote;
+ this.onNoteSelected = onNoteSelected;
function onLoad()
{
_dynamicFields = document.getElementById('editpane-dynamic-fields');
_creatorTypeMenu = document.getElementById('creatorTypeMenu');
- _notesPane = document.getElementById('scholar-notes');
+ _notesMenu = document.getElementById('scholar-notes-menu');
+ _notesField = document.getElementById('scholar-notes-field');
+ _notesLabel = document.getElementById('scholar-notes-label');
var creatorTypes = Scholar.CreatorTypes.getTypes();
for(var i = 0; i < creatorTypes.length; i++)
@@ -60,24 +68,18 @@ ScholarItemPane = new function()
for(var i = 0; i<fieldNames.length; i++)
{
- if(fieldNames[i] != 'notes')
- {
- var editable = (!_itemBeingEdited.isPrimaryField(fieldNames[i]) || _itemBeingEdited.isEditableField(fieldNames[i]));
-
- var valueElement = createValueElement(_itemBeingEdited.getField(fieldNames[i]), editable ? fieldNames[i] : null);
-
- var label = document.createElement("label");
- label.setAttribute("value",Scholar.getString("itemFields."+fieldNames[i])+":");
- label.setAttribute("onclick","this.nextSibling.blur();");
-
- addDynamicRow(label,valueElement);
- }
- else
- {
- _notesPane.value = _itemBeingEdited.getField(fieldNames[i]);
- }
+ var editable = (!_itemBeingEdited.isPrimaryField(fieldNames[i]) || _itemBeingEdited.isEditableField(fieldNames[i]));
+
+ var valueElement = createValueElement(_itemBeingEdited.getField(fieldNames[i]), editable ? fieldNames[i] : null);
+
+ var label = document.createElement("label");
+ label.setAttribute("value",Scholar.getString("itemFields."+fieldNames[i])+":");
+ label.setAttribute("onclick","this.nextSibling.blur();");
+
+ addDynamicRow(label,valueElement);
}
+ //CREATORS:
_beforeRow = _dynamicFields.firstChild.nextSibling;
_creatorCount = 0;
if(_itemBeingEdited.numCreators() > 0)
@@ -92,6 +94,19 @@ ScholarItemPane = new function()
{
addCreatorRow('', '', 1);
}
+
+ //NOTES:
+ _notesMenu.removeAllItems();
+
+ var notes = _itemBeingEdited.getNotes();
+ if(notes.length)
+ for(var i = 0; i < notes.length; i++)
+ _notesMenu.appendItem(_noteToTitle(_itemBeingEdited.getNote(notes[i])),notes[i]);
+ else
+ addNote();
+
+ _updateNoteCount();
+ _notesMenu.selectedIndex = 0;
}
function addDynamicRow(label, value, beforeElement)
@@ -248,6 +263,80 @@ ScholarItemPane = new function()
_itemBeingEdited.setCreator(index, firstName, lastName, typeID);
_itemBeingEdited.save();
}
+
+ function modifySelectedNote()
+ {
+ if(_notesMenu.selectedIndex == -1)
+ return;
+
+ var id = _notesMenu.selectedItem.value;
+ if(id)
+ {
+ _itemBeingEdited.updateNote(id,_notesField.value);
+ }
+ else //new note
+ {
+ id = _itemBeingEdited.addNote(_notesField.value);
+ _notesMenu.selectedItem.value = id;
+ }
+ var label = _notesField.value;
+
+ _notesMenu.selectedItem.label = _noteToTitle(_notesField.value);
+ }
+
+ function removeSelectedNote()
+ {
+ var id = _notesMenu.selectedItem.value;
+ if(id)
+ {
+ _itemBeingEdited.removeNote(id);
+ }
+ _notesMenu.removeitemAt(_notesMenu.selectedIndex);
+
+ if(_notesMenu.firstChild.childNodes.length == 0)
+ addNote();
+
+ _updateNoteCount();
+ }
+
+ function addNote()
+ {
+ modifySelectedNote();
+ _notesMenu.appendItem('Untitled Note');
+ _notesMenu.selectedIndex = _notesMenu.firstChild.childNodes.length-1;
+
+ _updateNoteCount();
+ }
+
+ function onNoteSelected()
+ {
+ var id = _notesMenu.selectedItem.value;
+ if(id)
+ _notesField.value = _itemBeingEdited.getNote(id);
+ else
+ _notesField.value = "";
+ }
+
+ function _noteToTitle(text)
+ {
+ var t = text.substring(0, Math.min(text.indexOf("\n"), 30) );
+
+ if(t == "")
+ {
+ return "Untitled Note";
+ }
+ else
+ {
+ return t;
+ }
+ }
+
+ function _updateNoteCount()
+ {
+ var c = _notesMenu.firstChild.childNodes.length;
+
+ _notesLabel.value = c + " note" + (c != 1 ? "s" : "") + ":";
+ }
}
addEventListener("load", function(e) { ScholarItemPane.onLoad(e); }, false);
diff --git a/chrome/chromeFiles/content/scholar/itemPane.xul b/chrome/chromeFiles/content/scholar/itemPane.xul
@@ -12,8 +12,16 @@
</tabs>
<tabpanels flex="1">
<vbox>
- <textbox id="scholar-notes"
- type="timed" timeout="1000" oncommand="ScholarItemPane.modifyField('notes',this.value);"
+ <hbox align="center">
+ <label id="scholar-notes-label"/>
+ <menulist flex="1" id="scholar-notes-menu" onselect="ScholarItemPane.onNoteSelected();">
+ <menupopup/>
+ </menulist>
+ <toolbarbutton label="-" class="addremove" oncommand="ScholarItemPane.removeSelectedNote();"/>
+ <toolbarbutton label="+" class="addremove" oncommand="ScholarItemPane.addNote();"/>
+ </hbox>
+ <textbox id="scholar-notes-field"
+ type="timed" timeout="1000" oncommand="ScholarItemPane.modifySelectedNote();"
multiline="true" flex="1"
onblur="ScholarItemPane.modifyField('notes',this.value);"/>
</vbox>
diff --git a/chrome/chromeFiles/content/scholar/itemTreeView.js b/chrome/chromeFiles/content/scholar/itemTreeView.js
@@ -1,3 +1,14 @@
+////////////////////////////////////////////////////////////////////////////////
+///
+/// ItemTreeView
+/// -- handles the link between an individual tree and the data layer
+/// -- displays only items (no collections, no hierarchy)
+///
+////////////////////////////////////////////////////////////////////////////////
+
+/*
+ * Constructor the the ItemTreeView object
+ */
Scholar.ItemTreeView = function(itemGroup)
{
this._itemGroup = itemGroup;
@@ -9,6 +20,9 @@ Scholar.ItemTreeView = function(itemGroup)
this._unregisterID = Scholar.Notifier.registerItemTree(this);
}
+/*
+ * Called by the tree itself
+ */
Scholar.ItemTreeView.prototype.setTree = function(treebox)
{
if(this._treebox)
@@ -25,6 +39,10 @@ Scholar.ItemTreeView.prototype.setTree = function(treebox)
}
}
+/*
+ * Reload the rows from the data access methods
+ * (doesn't call the tree.invalidate methods, etc.)
+ */
Scholar.ItemTreeView.prototype.refresh = function()
{
this._dataItems = new Array();
@@ -38,6 +56,9 @@ Scholar.ItemTreeView.prototype.refresh = function()
this._refreshHashMap();
}
+/*
+ * Called by Scholar.Notifier on any changes to items in the data layer
+ */
Scholar.ItemTreeView.prototype.notify = function(action, type, ids)
{
var madeChanges = false;
@@ -112,11 +133,21 @@ Scholar.ItemTreeView.prototype.notify = function(action, type, ids)
this.selection.selectEventsSuppressed = false;
}
+/*
+ * Unregisters view from Scholar.Notifier (called on window close)
+ */
Scholar.ItemTreeView.prototype.unregister = function()
{
Scholar.Notifier.unregisterItemTree(this._unregisterID);
}
+////////////////////////////////////////////////////////////////////////////////
+///
+/// nsITreeView functions
+/// http://www.xulplanet.com/references/xpcomref/ifaces/nsITreeView.html
+///
+////////////////////////////////////////////////////////////////////////////////
+
Scholar.ItemTreeView.prototype.getCellText = function(row, column)
{
var obj = this._getItemAtRow(row);
@@ -175,9 +206,12 @@ Scholar.ItemTreeView.prototype.cycleHeader = function(column)
this._treebox.invalidate();
}
+/*
+ * Sort the items by the currently sorted column.
+ * Simply uses Array.sort() function, and refreshes the hash map.
+ */
Scholar.ItemTreeView.prototype.sort = function()
{
-
var column = this._treebox.columns.getSortedColumn()
var order = column.element.getAttribute('sortDirection') == 'descending';
@@ -199,13 +233,13 @@ Scholar.ItemTreeView.prototype.sort = function()
}
}
- function oppSort(a,b)
+ function oppositeSort(a,b)
{
return(columnSort(a,b) * -1);
}
if(order)
- this._dataItems.sort(oppSort);
+ this._dataItems.sort(oppositeSort);
else
this._dataItems.sort(columnSort);
@@ -213,6 +247,15 @@ Scholar.ItemTreeView.prototype.sort = function()
}
+////////////////////////////////////////////////////////////////////////////////
+///
+/// Additional functions for managing data in the tree
+///
+////////////////////////////////////////////////////////////////////////////////
+
+/*
+ * Delete the selection
+ */
Scholar.ItemTreeView.prototype.deleteSelection = function()
{
if(this.selection.count == 0)
@@ -246,6 +289,9 @@ Scholar.ItemTreeView.prototype.deleteSelection = function()
this._treebox.endUpdateBatch();
}
+/*
+ * Set the search filter on the view
+ */
Scholar.ItemTreeView.prototype.searchText = function(search)
{
this.selection.selectEventsSuppressed = true;
@@ -263,23 +309,35 @@ Scholar.ItemTreeView.prototype.searchText = function(search)
this._treebox.invalidate();
}
+/*
+ * Called by various view functions to show a row
+ *
+ * item: reference to the Item
+ * beforeRow: row index to insert new row before
+ */
Scholar.ItemTreeView.prototype._showItem = function(item, beforeRow)
{
this._dataItems.splice(beforeRow, 0, item); this.rowCount++;
}
+/*
+ * Called by view to hide specified row
+ */
Scholar.ItemTreeView.prototype._hideItem = function(row)
{
this._dataItems.splice(row,1); this.rowCount--;
}
+/*
+ * Returns a reference to the item at row (see Scholar.Item in data_access.js)
+ */
Scholar.ItemTreeView.prototype._getItemAtRow = function(row)
{
return this._dataItems[row];
}
/*
- * Create hash map of item ids to row indexes
+ * Create hash map of item ids to row indexes
*/
Scholar.ItemTreeView.prototype._refreshHashMap = function()
{
@@ -288,6 +346,9 @@ Scholar.ItemTreeView.prototype._refreshHashMap = function()
this._itemRowMap[this._getItemAtRow(i).getID()] = i;
}
+/*
+ * Saves the ids of currently selected items for later
+ */
Scholar.ItemTreeView.prototype.saveSelection = function()
{
this._savedSelection = new Array();
@@ -302,6 +363,9 @@ Scholar.ItemTreeView.prototype.saveSelection = function()
}
}
+/*
+ * Sets the selection based on saved selection ids (see above)
+ */
Scholar.ItemTreeView.prototype.rememberSelection = function()
{
this.selection.clearSelection();
@@ -312,15 +376,26 @@ Scholar.ItemTreeView.prototype.rememberSelection = function()
}
}
-/* DRAG AND DROP FUNCTIONS */
+////////////////////////////////////////////////////////////////////////////////
+///
+/// Drag-and-drop functions:
+/// for nsDragAndDrop.js + nsTransferable.js
+///
+////////////////////////////////////////////////////////////////////////////////
+/*
+ * Begin a drag
+ */
Scholar.ItemTreeView.prototype.onDragStart = function (evt,transferData,action)
{
transferData.data=new TransferData();
this.saveSelection();
transferData.data.addDataForFlavour("scholar/item",this._savedSelection);
}
-
+
+/*
+ * Called by nsDragAndDrop.js for any sort of drop on the tree
+ */
Scholar.ItemTreeView.prototype.getSupportedFlavours = function ()
{
var flavors = new FlavourSet();
@@ -329,28 +404,39 @@ Scholar.ItemTreeView.prototype.getSupportedFlavours = function ()
return flavors;
}
+/*
+ * Called by nsDragAndDrop.js for any sort of drop on the tree
+ */
Scholar.ItemTreeView.prototype.onDrop = function (evt,data,session)
{
var dataType = data.flavour.contentType;
- var ids = data.data.split(',');
if(dataType == 'scholar/item')
{
+ var ids = data.data.split(',');
for(var i = 0; i<ids.length; i++)
this._itemGroup.ref.addItem(ids[i]);
}
else if(dataType == 'text/x-moz-url')
{
- alert(data.data);
- var targetCollection = this._itemGroup.ref;
- /*for(var i = 0; i<ids.length; i++)
- targetCollection.addItem(ids[i]); */
+ var url = data.data.split("\n")[0];
+
+ /* WAITING FOR INGESTER SUPPORT
+ var newItem = Scholar.Ingester.scrapeURL(url);
+
+ if(newItem)
+ this._itemGroup.ref.addItem(newItem.getID());
+ */
}
}
Scholar.ItemTreeView.prototype.onDragOver = function (evt,dropdata,session) { }
-/* MORE TREEVIEW FUNCTIONS THAT HAVE TO BE HERE */
+////////////////////////////////////////////////////////////////////////////////
+///
+/// Functions for nsITreeView that we have to stub out.
+///
+////////////////////////////////////////////////////////////////////////////////
Scholar.ItemTreeView.prototype.isSeparator = function(row) { return false; }
Scholar.ItemTreeView.prototype.isContainer = function(row) { return false; }