!import
1 //@line 38 "/home/visbrero/mnt/roisin/rev_control/hg/mozilla/mail/components/preferences/downloadactions.js"
2
3 const kRootTypePrefix = "urn:mimetype:";
4
5 ///////////////////////////////////////////////////////////////////////////////
6 // MIME Types Datasource RDF Utils
NC_URI
7 function NC_URI(aProperty)
8 {
9 return "http://home.netscape.com/NC-rdf#" + aProperty;
10 }
11
MIME_URI
12 function MIME_URI(aType)
13 {
14 return "urn:mimetype:" + aType;
15 }
16
HANDLER_URI
17 function HANDLER_URI(aHandler)
18 {
19 return "urn:mimetype:handler:" + aHandler;
20 }
21
APP_URI
22 function APP_URI(aType)
23 {
24 return "urn:mimetype:externalApplication:" + aType;
25 }
26
27 var gDownloadActionsWindow = {
28 _tree : null,
29 _editButton : null,
30 _removeButton : null,
31 _actions : [],
32 _plugins : {},
33 _bundle : null,
34 _pref : Components.classes["@mozilla.org/preferences-service;1"]
35 .getService(Components.interfaces.nsIPrefBranch),
36 _mimeSvc : Components.classes["@mozilla.org/mime;1"]
37 .getService(Components.interfaces.nsIMIMEService),
38 _excludingPlugins : false,
39 _excludingMissingExtensions : false,
40
init
41 init: function ()
42 {
43 (this._editButton = document.getElementById("editFileHandler")).disabled = true;
44 (this._removeButton = document.getElementById("removeFileHandler")).disabled = true;
45
46 // Initialize the File Type list
47 this._bundle = document.getElementById("bundlePreferences");
48 this._tree = document.getElementById("fileHandlersList");
49 this._loadView();
50 // Determine any exclusions being applied - e.g. don't show types for which
51 // only a plugin handler exists, don't show types lacking extensions, etc.
52 this._view._rowCount = this._updateExclusions();
53 this._tree.treeBoxObject.view = this._view;
54
55 var indexToSelect = parseInt(this._tree.getAttribute("lastSelected"));
56 if (indexToSelect < this._tree.view.rowCount)
57 this._tree.view.selection.select(indexToSelect);
58 this._tree.focus();
59 },
60
_loadView
61 _loadView: function ()
62 {
63 // Reset ALL the collections and state flags, because we can call this after
64 // the window has initially displayed by resetting the filter.
65 this._actions = [];
66 this._plugins = {};
67 this._view._filtered = false;
68 this._view._filterSet = [];
69 this._view._usingExclusionSet = false;
70 this._view._exclusionSet = [];
71 this._view._filterValue = "";
72
73 this._loadPluginData();
74 this._loadMIMERegistryData();
75 },
76
_updateRowCount
77 _updateRowCount: function (aNewRowCount)
78 {
79 var oldCount = this._view._rowCount;
80 this._view._rowCount = 0;
81 this._tree.treeBoxObject.rowCountChanged(0, -oldCount);
82 this._view._rowCount = aNewRowCount;
83 this._tree.treeBoxObject.rowCountChanged(0, aNewRowCount);
84 },
85
uninit
86 uninit: function ()
87 {
88 },
89
_updateExclusions
90 _updateExclusions: function ()
91 {
92 this._excludingPlugins = true;
93 this._excludingMissingExtensions = true;
94 this._view._exclusionSet = [].concat(this._actions);
95 if (this._excludingMissingExtensions) {
96 this._view._usingExclusionSet = true;
97 for (var i = 0; i < this._view._exclusionSet.length;) {
98 if (!this._view._exclusionSet[i].hasExtension)
99 this._view._exclusionSet.splice(i, 1);
100 else
101 ++i;
102 }
103 }
104 if (this._excludingPlugins) {
105 this._view._usingExclusionSet = true;
106 for (i = 0; i < this._view._exclusionSet.length;) {
107 if (this._view._exclusionSet[i].handledOnlyByPlugin)
108 this._view._exclusionSet.splice(i, 1);
109 else
110 ++i
111 }
112 }
113
114 return this._view._usingExclusionSet ? this._view._exclusionSet.length
115 : this._view._filtered ? this._view._filterSet.length
116 : this._actions.length;
117 },
118
_loadPluginData
119 _loadPluginData: function ()
120 {
121 },
122
_createAction
123 _createAction: function (aMIMEType, aActionName,
124 aIsEditable, aHandleMode, aCustomHandler,
125 aPluginAvailable, aPluginEnabled,
126 aHandledOnlyByPlugin)
127 {
128 var newAction = !(aMIMEType in this._plugins);
129 var action = newAction ? new FileAction() : this._plugins[aMIMEType];
130 action.type = aMIMEType;
131 var info = this._mimeSvc.getFromTypeAndExtension(action.type, null);
132
133 // File Extension
134 try {
135 action.extension = info.primaryExtension;
136 }
137 catch (e) {
138 action.extension = this._bundle.getString("extensionNone");
139 action.hasExtension = false;
140 }
141
142 // Large and Small Icon
143 try {
144 action.smallIcon = "moz-icon://goat." + info.primaryExtension + "?size=16";
145 action.bigIcon = "moz-icon://goat." + info.primaryExtension + "?size=32";
146 }
147 catch (e) {
148 action.smallIcon = "moz-icon://goat?size=16&contentType=" + info.MIMEType;
149 action.bigIcon = "moz-icon://goat?contentType=" + info.MIMEType + "&size=32";
150 }
151
152 // Pretty Type Name
153 if (info.description == "") {
154 try {
155 action.typeName = this._bundle.getFormattedString("fileEnding", [info.primaryExtension.toUpperCase()]);
156 }
157 catch (e) {
158 // Wow, this sucks, just show the MIME type as a last ditch effort to display
159 // the type of file that this is.
160 action.typeName = info.MIMEType;
161 }
162 }
163 else
164 action.typeName = info.description;
165
166 // Pretty Action Name
167 if (aActionName)
168 action.action = aActionName;
169 action.pluginAvailable = aPluginAvailable;
170 action.pluginEnabled = aPluginEnabled;
171 action.editable = aIsEditable;
172 action.handleMode = aHandleMode;
173 action.customHandler = aCustomHandler;
174 action.mimeInfo = info;
175 action.handledOnlyByPlugin = aHandledOnlyByPlugin
176
177 if (newAction && !(action.handledOnlyByPlugin && !action.pluginEnabled)) {
178 this._actions.push(action);
179 this._plugins[action.type] = action;
180 }
181 return action;
182 },
183
_loadMIMEDS
184 _loadMIMEDS: function ()
185 {
186 var fileLocator = Components.classes["@mozilla.org/file/directory_service;1"]
187 .getService(Components.interfaces.nsIProperties);
188
189 var file = fileLocator.get("UMimTyp", Components.interfaces.nsIFile);
190
191 var ioService = Components.classes["@mozilla.org/network/io-service;1"]
192 .getService(Components.interfaces.nsIIOService);
193 var fileHandler = ioService.getProtocolHandler("file")
194 .QueryInterface(Components.interfaces.nsIFileProtocolHandler);
195 this._mimeDS = this._rdf.GetDataSourceBlocking(fileHandler.getURLSpecFromFile(file));
196 },
197
_getLiteralValue
198 _getLiteralValue: function (aResource, aProperty)
199 {
200 var property = this._rdf.GetResource(NC_URI(aProperty));
201 var value = this._mimeDS.GetTarget(aResource, property, true);
202 if (value)
203 return value.QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
204 return "";
205 },
206
_getChildResource
207 _getChildResource: function (aResource, aProperty)
208 {
209 var property = this._rdf.GetResource(NC_URI(aProperty));
210 return this._mimeDS.GetTarget(aResource, property, true);
211 },
212
_getDisplayNameForFile
213 _getDisplayNameForFile: function (aFile)
214 {
215 //@line 261 "/home/visbrero/mnt/roisin/rev_control/hg/mozilla/mail/components/preferences/downloadactions.js"
216 // XXXben - Read the bundle name on OS X.
217 var ios = Components.classes["@mozilla.org/network/io-service;1"]
218 .getService(Components.interfaces.nsIIOService);
219 var url = ios.newFileURI(aFile).QueryInterface(Components.interfaces.nsIURL);
220 return url.fileName;
221 },
222
_loadMIMERegistryData
223 _loadMIMERegistryData: function ()
224 {
225 this._rdf = Components.classes["@mozilla.org/rdf/rdf-service;1"]
226 .getService(Components.interfaces.nsIRDFService);
227 this._loadMIMEDS();
228
229 var root = this._rdf.GetResource("urn:mimetypes:root");
230 var container = Components.classes["@mozilla.org/rdf/container;1"]
231 .createInstance(Components.interfaces.nsIRDFContainer);
232 container.Init(this._mimeDS, root);
233
234 var elements = container.GetElements();
235 while (elements.hasMoreElements()) {
236 var type = elements.getNext();
237 if (!(type instanceof Components.interfaces.nsIRDFResource))
238 break;
239 var editable = this._getLiteralValue(type, "editable") == "true";
240 if (!editable)
241 continue;
242
243 var handler = this._getChildResource(type, "handlerProp");
244 var alwaysAsk = this._getLiteralValue(handler, "alwaysAsk") == "true";
245 if (alwaysAsk)
246 continue;
247 var saveToDisk = this._getLiteralValue(handler, "saveToDisk") == "true";
248 var useSystemDefault = this._getLiteralValue(handler, "useSystemDefault") == "true";
249 var editable = this._getLiteralValue(type, "editable") == "true";
250 var handledInternally = this._getLiteralValue(handler, "handleInternal") == "true";
251 var externalApp = this._getChildResource(handler, "externalApplication");
252 var externalAppPath = this._getLiteralValue(externalApp, "path");
253 try {
254 var customHandler = Components.classes["@mozilla.org/file/local;1"]
255 .createInstance(Components.interfaces.nsILocalFile);
256 customHandler.initWithPath(externalAppPath);
257 }
258 catch (e) {
259 customHandler = null;
260 }
261 if (customHandler && !customHandler.exists())
262 customHandler = null;
263 var mimeType = this._getLiteralValue(type, "value");
264 var typeInfo = this._mimeSvc.getFromTypeAndExtension(mimeType, null);
265
266 // Determine the pretty name of the associated action.
267 var actionName = "";
268 var handleMode = 0;
269 if (saveToDisk) {
270 // Save the file to disk
271 actionName = this._bundle.getString("saveToDisk");
272 handleMode = FILEACTION_SAVE_TO_DISK;
273 }
274 else if (useSystemDefault) {
275 // Use the System Default handler
276 actionName = this._bundle.getFormattedString("openWith",
277 [typeInfo.defaultDescription]);
278 handleMode = FILEACTION_OPEN_DEFAULT;
279 }
280 else {
281 // Custom Handler
282 if (customHandler) {
283 actionName = this._bundle.getFormattedString("openWith",
284 [this._getDisplayNameForFile(customHandler)]);
285 handleMode = FILEACTION_OPEN_CUSTOM;
286 }
287 else {
288 // Corrupt datasource, invalid custom handler path. Revert to default.
289 actionName = this._bundle.getFormattedString("openWith",
290 [typeInfo.defaultDescription]);
291 handleMode = FILEACTION_OPEN_DEFAULT;
292 }
293 }
294
295 if (handledInternally)
296 handleMode = FILEACTION_OPEN_INTERNALLY;
297
298 var pluginAvailable = mimeType in this._plugins && this._plugins[mimeType].pluginAvailable;
299 var pluginEnabled = pluginAvailable && this._plugins[mimeType].pluginEnabled;
300 if (pluginEnabled) {
301 handleMode = FILEACTION_OPEN_PLUGIN;
302 actionName = null;
303 }
304 var action = this._createAction(mimeType, actionName, editable, handleMode,
305 customHandler, pluginAvailable, pluginEnabled,
306 false);
307 }
308 },
309
310 _view: {
311 _filtered : false,
312 _filterSet : [],
313 _usingExclusionSet : false,
314 _exclusionSet : [],
315 _filterValue : "",
316
317 _rowCount: 0,
get_rowCount
318 get rowCount()
319 {
320 return this._rowCount;
321 },
322
get_activeCollection
323 get activeCollection ()
324 {
325 return this._filtered ? this._filterSet
326 : this._usingExclusionSet ? this._exclusionSet
327 : gDownloadActionsWindow._actions;
328 },
329
getItemAtIndex
330 getItemAtIndex: function (aIndex)
331 {
332 return this.activeCollection[aIndex];
333 },
334
getCellText
335 getCellText: function (aIndex, aColumn)
336 {
337 switch (aColumn.id) {
338 case "fileExtension":
339 return this.getItemAtIndex(aIndex).extension.toUpperCase();
340 case "fileType":
341 return this.getItemAtIndex(aIndex).typeName;
342 case "fileMIMEType":
343 return this.getItemAtIndex(aIndex).type;
344 case "fileHandler":
345 return this.getItemAtIndex(aIndex).action;
346 }
347 return "";
348 },
getImageSrc
349 getImageSrc: function (aIndex, aColumn)
350 {
351 if (aColumn.id == "fileExtension")
352 return this.getItemAtIndex(aIndex).smallIcon;
353 return "";
354 },
355 _selection: null,
get_selection
356 get selection () { return this._selection; },
set_selection
357 set selection (val) { this._selection = val; return val; },
getRowProperties
358 getRowProperties: function (aIndex, aProperties) {},
getCellProperties
359 getCellProperties: function (aIndex, aColumn, aProperties) {},
getColumnProperties
360 getColumnProperties: function (aColumn, aProperties) {},
isContainer
361 isContainer: function (aIndex) { return false; },
isContainerOpen
362 isContainerOpen: function (aIndex) { return false; },
isContainerEmpty
363 isContainerEmpty: function (aIndex) { return false; },
isSeparator
364 isSeparator: function (aIndex) { return false; },
isSorted
365 isSorted: function (aIndex) { return false; },
canDrop
366 canDrop: function (aIndex, aOrientation) { return false; },
drop
367 drop: function (aIndex, aOrientation) {},
getParentIndex
368 getParentIndex: function (aIndex) { return -1; },
hasNextSibling
369 hasNextSibling: function (aParentIndex, aIndex) { return false; },
getLevel
370 getLevel: function (aIndex) { return 0; },
getProgressMode
371 getProgressMode: function (aIndex, aColumn) {},
getCellValue
372 getCellValue: function (aIndex, aColumn) {},
setTree
373 setTree: function (aTree) {},
toggleOpenState
374 toggleOpenState: function (aIndex) { },
375 cycleHeader: function (aColumn) {},
selectionChanged
376 selectionChanged: function () {},
cycleCell
377 cycleCell: function (aIndex, aColumn) {},
isEditable
378 isEditable: function (aIndex, aColumn) { return false; },
isSelectable
379 isSelectable: function (aIndex, aColumn) { return false; },
setCellValue
380 setCellValue: function (aIndex, aColumn, aValue) {},
setCellText
381 setCellText: function (aIndex, aColumn, aValue) {},
performAction
382 performAction: function (aAction) {},
performActionOnRow
383 performActionOnRow: function (aAction, aIndex) {},
performActionOnCell
384 performActionOnCell: function (aAction, aindex, aColumn) {}
385 },
386
removeFileHandler
387 removeFileHandler: function ()
388 {
389 var selection = this._tree.view.selection;
390 if (selection.count < 1)
391 return;
392
393 var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
394 .getService(Components.interfaces.nsIPromptService);
395 var flags = promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_0;
396 flags += promptService.BUTTON_TITLE_CANCEL * promptService.BUTTON_POS_1;
397
398 var title = this._bundle.getString("removeTitle" + (selection.count > 1 ? "Multiple" : "Single"));
399 var message = this._bundle.getString("removeMessage" + (selection.count > 1 ? "Multiple" : "Single"));
400 var button = this._bundle.getString("removeButton" + (selection.count > 1 ? "Multiple" : "Single"));
401 rv = promptService.confirmEx(window, title, message, flags, button,
402 null, null, null, { value: 0 });
403 if (rv != 0)
404 return;
405
406 var rangeCount = selection.getRangeCount();
407 var lastSelected = 0;
408 var mimeDSDirty = false;
409 for (var i = 0; i < rangeCount; ++i) {
410 var min = { }; var max = { };
411 selection.getRangeAt(i, min, max);
412 for (var j = min.value; j <= max.value; ++j) {
413 var item = this._view.getItemAtIndex(j);
414 if (!item.handledOnlyByPlugin) {
415 // There is data for this type in the MIME registry, so make sure we
416 // remove it from the MIME registry. We don't disable the plugin here because
417 // if we do there's currently no way through the UI to re-enable it. We may
418 // come up with some sort of solution for that at a later date.
419 var typeRes = this._rdf.GetResource(MIME_URI(item.type));
420 var handlerRes = this._getChildResource(typeRes, "handlerProp");
421 var extAppRes = this._getChildResource(handlerRes, "externalApplication");
422 this._cleanResource(extAppRes);
423 this._cleanResource(handlerRes);
424 this._cleanResource(typeRes);
425 mimeDSDirty = true;
426 }
427 lastSelected = (j + 1) >= this._view.rowCount ? j-1 : j;
428 }
429 }
430 if (mimeDSDirty &&
431 this._mimeDS instanceof Components.interfaces.nsIRDFRemoteDataSource)
432 this._mimeDS.Flush();
433
434 // Just reload the list to make sure deletions are respected
435 this._loadView();
436 this._updateRowCount(this._updateExclusions());
437
438 selection.select(lastSelected);
439 },
440
_cleanResource
441 _cleanResource: function (aResource)
442 {
443 var labels = this._mimeDS.ArcLabelsOut(aResource);
444 while (labels.hasMoreElements()) {
445 var arc = labels.getNext();
446 if (!(arc instanceof Components.interfaces.nsIRDFResource))
447 break;
448 var target = this._mimeDS.GetTarget(aResource, arc, true);
449 this._mimeDS.Unassert(aResource, arc, target);
450 }
451 },
452
_disablePluginForItem
453 _disablePluginForItem: function (aItem)
454 {
455 },
456
_enablePluginForItem
457 _enablePluginForItem: function (aItem)
458 {
459 },
460
_ensureMIMERegistryEntry
461 _ensureMIMERegistryEntry: function (aItem)
462 {
463 var root = this._rdf.GetResource("urn:mimetypes:root");
464 var container = Components.classes["@mozilla.org/rdf/container;1"]
465 .createInstance(Components.interfaces.nsIRDFContainer);
466 container.Init(this._mimeDS, root);
467
468 var itemResource = this._rdf.GetResource(MIME_URI(aItem.type));
469 var handlerResource = null;
470 if (container.IndexOf(itemResource) == -1) {
471 container.AppendElement(itemResource);
472 this._setLiteralValue(itemResource, "editable", "true");
473 this._setLiteralValue(itemResource, "value", aItem.type);
474
475 handlerResource = this._rdf.GetResource(HANDLER_URI(aItem.type));
476 this._setLiteralValue(handlerResource, "alwaysAsk", "false");
477 var handlerProp = this._rdf.GetResource(NC_URI("handlerProp"));
478 this._mimeDS.Assert(itemResource, handlerProp, handlerResource, true);
479
480 var extAppResource = this._rdf.GetResource(APP_URI(aItem.type));
481 this._setLiteralValue(extAppResource, "path", "");
482 var extAppProp = this._rdf.GetResource(NC_URI("externalApplication"));
483 this._mimeDS.Assert(handlerResource, extAppProp, extAppResource, true);
484 }
485 else
486 handlerResource = this._getChildResource(itemResource, "handlerProp");
487
488 return handlerResource;
489 },
490
_setLiteralValue
491 _setLiteralValue: function (aResource, aProperty, aValue)
492 {
493 var property = this._rdf.GetResource(NC_URI(aProperty));
494 var newValue = this._rdf.GetLiteral(aValue);
495 var oldValue = this._mimeDS.GetTarget(aResource, property, true);
496 if (oldValue)
497 this._mimeDS.Change(aResource, property, oldValue, newValue);
498 else
499 this._mimeDS.Assert(aResource, property, newValue, true);
500 },
501
editFileHandler
502 editFileHandler: function ()
503 {
504 var selection = this._tree.view.selection;
505 if (selection.count != 1)
506 return;
507
508 var item = this._view.getItemAtIndex(selection.currentIndex);
509 openDialog("chrome://messenger/content/preferences/changeaction.xul",
510 "_blank", "modal,centerscreen", item);
511
512 // Update the database
513 switch (item.handleMode) {
514 case FILEACTION_OPEN_PLUGIN:
515 this._enablePluginForItem(item);
516 // We don't need to adjust the database because plugin settings always
517 // supercede whatever is in the db, leaving it untouched allows the last
518 // user setting(s) to be preserved if they ever revert.
519 break;
520 case FILEACTION_OPEN_DEFAULT:
521 this._disablePluginForItem(item);
522 var handlerRes = this._ensureMIMERegistryEntry(item);
523 this._setLiteralValue(handlerRes, "useSystemDefault", "true");
524 this._setLiteralValue(handlerRes, "saveToDisk", "false");
525 break;
526 case FILEACTION_OPEN_CUSTOM:
527 this._disablePluginForItem(item);
528 var handlerRes = this._ensureMIMERegistryEntry(item);
529 this._setLiteralValue(handlerRes, "useSystemDefault", "false");
530 this._setLiteralValue(handlerRes, "saveToDisk", "false");
531 var extAppRes = this._getChildResource(handlerRes, "externalApplication");
532 this._setLiteralValue(extAppRes, "path", item.customHandler.path);
533 break;
534 case FILEACTION_SAVE_TO_DISK:
535 this._disablePluginForItem(item);
536 var handlerRes = this._ensureMIMERegistryEntry(item);
537 this._setLiteralValue(handlerRes, "useSystemDefault", "false");
538 this._setLiteralValue(handlerRes, "saveToDisk", "true");
539 break;
540 }
541
542 if (this._mimeDS instanceof Components.interfaces.nsIRDFRemoteDataSource)
543 this._mimeDS.Flush();
544
545 // Update the view
546 this._tree.treeBoxObject.invalidateRow(selection.currentIndex);
547 },
548
onSelectionChanged
549 onSelectionChanged: function ()
550 {
551 if (this._tree.view.rowCount == 0)
552 return;
553
554 var selection = this._tree.view.selection;
555 var selected = selection.count;
556 this._removeButton.disabled = selected == 0;
557 this._editButton.disabled = selected != 1;
558 var stringKey = selected > 1 ? "removeButtonMultiple" : "removeButtonSingle";
559 this._removeButton.label = this._bundle.getString(stringKey);
560
561 var canRemove = true;
562 var canEdit = true;
563
564 var rangeCount = selection.getRangeCount();
565 var min = { }, max = { };
566 var setLastSelected = false;
567 for (var i = 0; i < rangeCount; ++i) {
568 selection.getRangeAt(i, min, max);
569
570 for (var j = min.value; j <= max.value; ++j) {
571 if (!setLastSelected) {
572 // Set the last selected index to the first item in the selection
573 this._tree.setAttribute("lastSelected", j);
574 setLastSelected = true;
575 }
576
577 var item = this._view.getItemAtIndex(j);
578 if (item &&
579 (!item.editable || item.handleMode == FILEACTION_OPEN_INTERNALLY))
580 canEdit = false;
581
582 if (item &&
583 (!item.editable || item.handleMode == FILEACTION_OPEN_INTERNALLY ||
584 item.handledOnlyByPlugin))
585 canRemove = false;
586 }
587 }
588
589 if (!canRemove)
590 this._removeButton.disabled = true;
591 if (!canEdit)
592 this._editButton.disabled = true;
593 },
594
595 _lastSortProperty : "",
596 _lastSortAscending: false,
sort
597 sort: function (aProperty)
598 {
599 var ascending = (aProperty == this._lastSortProperty) ? !this._lastSortAscending : true;
sortByProperty
600 function sortByProperty(a, b)
601 {
602 return a[aProperty].toLowerCase().localeCompare(b[aProperty].toLowerCase());
603 }
sortByExtension
604 function sortByExtension(a, b)
605 {
606 if (!a.hasExtension && b.hasExtension)
607 return 1;
608 if (!b.hasExtension && a.hasExtension)
609 return -1;
610 return a.extension.toLowerCase().localeCompare(b.extension.toLowerCase());
611 }
612 // Sort the Filtered List, if in Filtered mode
613 if (!this._view._filtered) {
614 this._view.activeCollection.sort(aProperty == "extension" ? sortByExtension : sortByProperty);
615 if (!ascending)
616 this._view.activeCollection.reverse();
617 }
618
619 this._view.selection.clearSelection();
620 this._view.selection.select(0);
621 this._tree.treeBoxObject.invalidate();
622 this._tree.treeBoxObject.ensureRowIsVisible(0);
623
624 this._lastSortAscending = ascending;
625 this._lastSortProperty = aProperty;
626 },
627
clearFilter
628 clearFilter: function ()
629 {
630 // Clear the Filter and the Tree Display
631 document.getElementById("filter").value = "";
632 this._view._filtered = false;
633 this._view._filterSet = [];
634
635 // Just reload the list to make sure deletions are respected
636 this._loadView();
637 this._updateRowCount(this._updateExclusions());
638
639 // Restore selection
640 this._view.selection.clearSelection();
641 for (i = 0; i < this._lastSelectedRanges.length; ++i) {
642 var range = this._lastSelectedRanges[i];
643 this._view.selection.rangedSelect(range.min, range.max, true);
644 }
645 this._lastSelectedRanges = [];
646
647 document.getElementById("actionsIntro").value = this._bundle.getString("actionsAll");
648 document.getElementById("clearFilter").disabled = true;
649 },
650
_actionMatchesFilter
651 _actionMatchesFilter: function (aAction)
652 {
653 return aAction.extension.toLowerCase().indexOf(this._view._filterValue) != -1 ||
654 aAction.typeName.toLowerCase().indexOf(this._view._filterValue) != -1 ||
655 aAction.type.toLowerCase().indexOf(this._view._filterValue) != -1 ||
656 aAction.action.toLowerCase().indexOf(this._view._filterValue) != -1;
657 },
658
_filterActions
659 _filterActions: function (aFilterValue)
660 {
661 this._view._filterValue = aFilterValue;
662 var actions = [];
663 var collection = this._view._usingExclusionSet ? this._view._exclusionSet : this._actions;
664 for (var i = 0; i < collection.length; ++i) {
665 var action = collection[i];
666 if (this._actionMatchesFilter(action))
667 actions.push(action);
668 }
669 return actions;
670 },
671
672 _lastSelectedRanges: [],
_saveState
673 _saveState: function ()
674 {
675 // Save selection
676 var seln = this._view.selection;
677 this._lastSelectedRanges = [];
678 var rangeCount = seln.getRangeCount();
679 for (var i = 0; i < rangeCount; ++i) {
680 var min = {}; var max = {};
681 seln.getRangeAt(i, min, max);
682 this._lastSelectedRanges.push({ min: min.value, max: max.value });
683 }
684 },
685
686 _filterTimeout: -1,
onFilterInput
687 onFilterInput: function ()
688 {
689 if (this._filterTimeout != -1)
690 clearTimeout(this._filterTimeout);
691
filterActions
692 function filterActions()
693 {
694 var filter = document.getElementById("filter").value;
695 if (filter == "") {
696 gDownloadActionsWindow.clearFilter();
697 return;
698 }
699 var view = gDownloadActionsWindow._view;
700 view._filterSet = gDownloadActionsWindow._filterActions(filter);
701 if (!view._filtered) {
702 // Save Display Info for the Non-Filtered mode when we first
703 // enter Filtered mode.
704 gDownloadActionsWindow._saveState();
705 view._filtered = true;
706 }
707
708 // Clear the display
709 gDownloadActionsWindow._updateRowCount(view._filterSet.length);
710
711 view.selection.select(0);
712 document.getElementById("actionsIntro").value = gDownloadActionsWindow._bundle.getString("actionsFiltered");
713 document.getElementById("clearFilter").disabled = false;
714 }
715 window.filterActions = filterActions;
716 this._filterTimeout = setTimeout("filterActions();", 500);
717 },
718
onFilterKeyPress
719 onFilterKeyPress: function (aEvent)
720 {
721 if (aEvent.keyCode == 27) // ESC key
722 this.clearFilter();
723 },
724
focusFilterBox
725 focusFilterBox: function ()
726 {
727 var filter = document.getElementById("filter");
728 filter.focus();
729 filter.select();
730 }
731 };