--- a/suite/mailnews/content/FilterListDialog.js
+++ b/suite/mailnews/content/FilterListDialog.js
@@ -14,17 +14,18 @@ var gTopButton;
var gUpButton;
var gDownButton;
var gBottomButton;
var gRunFiltersFolderPrefix;
var gRunFiltersFolder;
var gRunFiltersButton;
var gFilterBundle;
var gFilterListMsgWindow = null;
-var gFilterTree;
+var gFilterListbox;
+var gCurrentFilterList;
var gStatusBar;
var gStatusText;
var gServerMenu;
var msgMoveMotion = {
Up : 0,
Down : 1,
Top : 2,
@@ -58,116 +59,47 @@ var gStatusFeedback = {
showProgress: function(percentage)
{
},
closeWindow: function()
{
}
};
-var gFilterTreeView = {
- mTree: null,
- get tree() {
- return this.mTree;
- },
- mFilterList: null,
- get filterList() {
- return this.mFilterList;
- },
- set filterList(val) {
- if (this.mTree)
- this.mTree.beginUpdateBatch();
- if (this.selection) {
- this.selection.clearSelection();
- this.selection.currentIndex = -1;
- }
- this.mFilterList = val;
- if (this.mTree) {
- this.mTree.scrollToRow(0);
- this.mTree.endUpdateBatch();
- }
- },
- /* nsITreeView methods */
- get rowCount() {
- return this.mFilterList ? this.mFilterList.filterCount : 0;
- },
- selection: null,
- getRowProperties: function getRowProperties(row) {
- return this.mFilterList.getFilterAt(row).enabled ? "Enabled-true" : "";
- },
- getCellProperties: function getCellProperties(row, col) {
- return this.mFilterList.getFilterAt(row).enabled ? "Enabled-true" : "";
- },
- getColumnProperties: function getColumnProperties(col) { return ""; },
- isContainer: function isContainer(index) { return false; },
- isContainerOpen: function isContainerOpen(index) { return false; },
- isContainerEmpty: function isContainerEmpty(index) { return false; },
- isSeparator: function isSeparator(index) { return false; },
- isSorted: function isSorted() { return false; },
- canDrop: function canDrop(index, orientation) { return false; },
- drop: function drop(index, orientation) {},
- getParentIndex: function getParentIndex(index) { return -1; },
- hasNextSibling: function hasNextSibling(rowIndex, afterIndex) { return false; },
- getLevel: function getLevel(index) { return 0; },
- getImageSrc: function getImageSrc(row, col) { return null; },
- getProgressMode: function getProgressMode(row, col) { return 0; },
- getCellValue: function getCellValue(row, col) { return null; },
- getCellText: function getCellText(row, col) {
- return this.mFilterList.getFilterAt(row).filterName;
- },
- setTree: function setTree(tree) {
- this.mTree = tree;
- },
- toggleOpenState: function toggleOpenState(index) {},
- cycleHeader: function cycleHeader(col) {},
- selectionChanged: function selectionChanged() {},
- cycleCell: function cycleCell(row, col) {
- if (toggleFilter(row))
- this.mTree.invalidateCell(row, col);
- },
- isEditable: function isEditable(row, col) { return false; }, // XXX Fix me!
- isSelectable: function isSelectable(row, col) { return false; },
- setCellValue: function setCellValue(row, col, value) {},
- setCellText: function setCellText(row, col, value) { /* XXX Write me */ },
-}
-
function onLoad()
{
setHelpFileURI("chrome://communicator/locale/help/suitehelp.rdf");
gFilterListMsgWindow = Cc["@mozilla.org/messenger/msgwindow;1"].createInstance(Ci.nsIMsgWindow);
gFilterListMsgWindow.domWindow = window;
gFilterListMsgWindow.rootDocShell.appType = Ci.nsIDocShell.APP_TYPE_MAIL;
gFilterListMsgWindow.statusFeedback = gStatusFeedback;
gFilterBundle = document.getElementById("bundle_filter");
gServerMenu = document.getElementById("serverMenu");
- gFilterTree = document.getElementById("filterTree");
+ gFilterListbox = document.getElementById("filterList");
gEditButton = document.getElementById("editButton");
gDeleteButton = document.getElementById("deleteButton");
gNewButton = document.getElementById("newButton");
gCopyToNewButton = document.getElementById("copyToNewButton");
gTopButton = document.getElementById("reorderTopButton");
gUpButton = document.getElementById("reorderUpButton");
gDownButton = document.getElementById("reorderDownButton");
gBottomButton = document.getElementById("reorderBottomButton");
gRunFiltersFolderPrefix = document.getElementById("folderPickerPrefix");
gRunFiltersFolder = document.getElementById("runFiltersFolder");
gRunFiltersButton = document.getElementById("runFiltersButton");
gStatusBar = document.getElementById("statusbar-icon");
gStatusText = document.getElementById("statusText");
- gFilterTree.view = gFilterTreeView;
+ updateButtons();
processWindowArguments(window.arguments[0]);
- // Focus the list.
- gFilterTree.focus();
-
Services.obs.addObserver(onFilterClose,
"quit-application-requested");
top.controllers.insertControllerAt(0, gFilterController);
}
/**
* Processes arguments sent to this dialog when opened or refreshed.
@@ -196,21 +128,23 @@ function processWindowArguments(aArgumen
if (!firstItem) {
var server = getServerThatCanHaveFilters();
if (server)
firstItem = server.rootFolder;
}
if (firstItem)
setFilterFolder(firstItem);
- else
- updateButtons();
if (wantedFolder)
setRunFolder(wantedFolder);
+ } else {
+ // If we didn't change folder still redraw the list
+ // to show potential new filters if we were called for refresh.
+ rebuildFilterList();
}
}
/**
* This is called from OpenOrFocusWindow() if the dialog is already open.
* New filters could have been created by operations outside the dialog.
*
* @param aArguments An object of arguments having the same format
@@ -234,32 +168,32 @@ function CanRunFiltersAfterTheFact(aServ
* (or a folder for NNTP server).
*/
function setFilterFolder(msgFolder) {
if (!msgFolder || msgFolder == gServerMenu._folder)
return;
// Save the current filters to disk before switching because
// the dialog may be closed and we'll lose current filters.
- let filterList = currentFilterList();
- if (filterList)
- filterList.saveToDefaultFile();
+ if (gCurrentFilterList)
+ gCurrentFilterList.saveToDefaultFile();
// Setting this attribute should go away in bug 473009.
gServerMenu._folder = msgFolder;
// Calling this should go away in bug 802609.
gServerMenu.menupopup.selectFolder(msgFolder);
- // Calling getFilterList will detect any errors in rules.dat,
- // backup the file, and alert the user
- gFilterTreeView.filterList = msgFolder.getEditableFilterList(gFilterListMsgWindow);
+ // Calling getEditableFilterList will detect any errors in
+ // msgFilterRules.dat, backup the file, and alert the user.
+ gCurrentFilterList = msgFolder.getEditableFilterList(gFilterListMsgWindow);
+ rebuildFilterList();
// Select the first item in the list, if there is one.
- if (gFilterTreeView.rowCount)
- gFilterTreeView.selection.select(0);
+ if (gFilterListbox.itemCount > 0)
+ gFilterListbox.selectItem(gFilterListbox.getItemAtIndex(0));
// This will get the deferred to account root folder, if server is deferred.
// We intentionally do this after setting the current server, as we want
// that to refer to the rootFolder for the actual server, not the
// deferred-to server, as current server is really a proxy for the
// server whose filters we are editing. But below here we are managing
// where the filters will get applied, which is on the deferred-to server.
msgFolder = msgFolder.server.rootMsgFolder;
@@ -322,63 +256,65 @@ function setFilterFolder(msgFolder) {
function setRunFolder(aFolder) {
// Setting this attribute should go away in bug 473009.
gRunFiltersFolder._folder = aFolder;
// Calling this should go away in bug 802609.
gRunFiltersFolder.menupopup.selectFolder(gRunFiltersFolder._folder);
updateButtons();
}
-function toggleFilter(index)
+/**
+ * Toggle enabled state of a filter, in both the filter properties and the UI.
+ *
+ * @param aFilterItem an item (row) of the filter list to be toggled
+ */
+function toggleFilter(aFilterItem)
{
- var filter = getFilter(index);
- if (filter.unparseable)
- {
- Services.prompt.alert(window, null,
- gFilterBundle.getFormattedString("cannotEnableIncompatFilter",
- [document.getElementById("bundle_brand").getString("brandShortName")]));
- return false;
- }
- filter.enabled = !filter.enabled;
- return true;
+ let filter = aFilterItem._filter;
+ if (filter.unparseable && !filter.enabled)
+ {
+ Services.prompt.alert(window, null,
+ gFilterBundle.getFormattedString("cannotEnableIncompatFilter",
+ [document.getElementById("bundle_brand").getString("brandShortName")]));
+ return;
+ }
+ filter.enabled = !filter.enabled;
+
+ // Now update the checkbox
+ aFilterItem.childNodes[1].setAttribute("enabled", filter.enabled);
+ // For accessibility set the checked state on listitem
+ aFilterItem.setAttribute("aria-checked", filter.enabled);
}
-function getFilter(index)
-{
- return gFilterTreeView.filterList.getFilterAt(index);
-}
-
+/**
+ * Returns the currently selected filter. If multiple filters are selected,
+ * returns the first one. If none are selected, returns null.
+ */
function currentFilter()
{
- var currentIndex = gFilterTree.currentIndex;
- return currentIndex == -1 ? null : getFilter(currentIndex);
-}
-
-function currentFilterList()
-{
- return gFilterTreeView.filterList;
-}
-
-function onFilterSelect(event)
-{
- updateButtons();
+ let currentItem = gFilterListbox.selectedItem;
+ return currentItem ? currentItem._filter : null;
}
function onEditFilter()
{
if (gEditButton.disabled)
return;
var selectedFilter = currentFilter();
- var curFilterList = currentFilterList();
- var args = {filter: selectedFilter, filterList: curFilterList};
+ if (!selectedFilter)
+ return;
+
+ let args = {filter: selectedFilter, filterList: gCurrentFilterList};
window.openDialog("chrome://messenger/content/FilterEditor.xul", "FilterEditor", "chrome,modal,titlebar,resizable,centerscreen", args);
- // The focus change will cause a repaint of the row updating any name change
+ if ("refresh" in args && args.refresh) {
+ rebuildFilterList();
+ }
}
/**
* Handler function for the 'New...' buttons.
* Opens the filter dialog for creating a new filter.
*/
function onNewFilter() {
calculatePositionAndShowCreateFilterDialog({});
@@ -404,69 +340,88 @@ function onCopyToNewFilter() {
* and then displays the create dialog.
*
* @param args The object containing the arguments for the dialog,
* passed to the filterEditorOnLoad() function.
* It will be augmented with the insertion position
* and global filters list properties by this function.
*/
function calculatePositionAndShowCreateFilterDialog(args) {
- var position = Math.max(gFilterTree.currentIndex, 0);
- args.filterList = currentFilterList();
+ let selectedFilter = currentFilter();
+ // If no filter is selected use the first position.
+ let position = 0;
+ if (selectedFilter) {
+ // Get the position in the unfiltered list.
+ // - this is where the new filter should be inserted!
+ let filterCount = gCurrentFilterList.filterCount;
+ for (let i = 0; i < filterCount; i++) {
+ if (gCurrentFilterList.getFilterAt(i) == selectedFilter) {
+ position = i;
+ break;
+ }
+ }
+ }
args.filterPosition = position;
+ args.filterList = gCurrentFilterList;
args.refresh = false;
window.openDialog("chrome://messenger/content/FilterEditor.xul",
"FilterEditor",
"chrome,modal,titlebar,resizable,centerscreen", args);
if (args.refresh)
{
- gFilterTreeView.tree.rowCountChanged(position, 1);
- gFilterTree.view.selection.select(position);
- gFilterTree.treeBoxObject.ensureRowIsVisible(position);
+ rebuildFilterList();
+
+ // Select the new filter, it is at the position of previous selection.
+ gFilterListbox.selectItem(gFilterListbox.getItemAtIndex(position));
+ if (currentFilter() != args.newFilter)
+ Cu.reportError("Filter created at an unexpected position!");
}
}
function onDeleteFilter()
{
if (gDeleteButton.disabled)
return;
- var filterList = currentFilterList();
- if (!filterList)
- return;
-
- var sel = gFilterTree.view.selection;
- var selCount = sel.getRangeCount();
- if (!selCount)
+ let items = gFilterListbox.selectedItems;
+ if (!items.length)
return;
let checkValue = {value: false};
if (Services.prefs.getBoolPref("mailnews.filters.confirm_delete") &&
Services.prompt.confirmEx(window, null,
gFilterBundle.getString("deleteFilterConfirmation"),
Services.prompt.STD_YES_NO_BUTTONS,
'', '', '',
gFilterBundle.getString('dontWarnAboutDeleteCheckbox'),
checkValue))
return;
if (checkValue.value)
Services.prefs.setBoolPref("mailnews.filters.confirm_delete", false);
- for (var i = selCount - 1; i >= 0; --i) {
- var start = {}, end = {};
- sel.getRangeAt(i, start, end);
- for (var j = end.value; j >= start.value; --j) {
- var curFilter = getFilter(j);
- if (curFilter)
- filterList.removeFilter(curFilter);
- }
- gFilterTreeView.tree.rowCountChanged(start.value, start.value - end.value - 1);
+ // Save filter position before the first selected one.
+ let newSelectionIndex = gFilterListbox.selectedIndex - 1;
+
+ // Must reverse the loop, as the items list shrinks when we delete.
+ for (let index = items.length - 1; index >= 0; --index) {
+ let item = items[index];
+ gCurrentFilterList.removeFilter(item._filter);
+ gFilterListbox.removeItemAt(gFilterListbox.getIndexOfItem(item));
+ }
+
+ // Select filter above previously selected if one existed,
+ // otherwise the first one.
+ if (newSelectionIndex == -1 && gFilterListbox.itemCount > 0)
+ newSelectionIndex = 0;
+ if (newSelectionIndex > -1) {
+ gFilterListbox.selectedIndex = newSelectionIndex;
+ updateViewPosition(-1);
}
}
/**
* Move filter one step up in visible list.
*/
function onUp(event) {
moveFilter(msgMoveMotion.Up);
@@ -501,57 +456,57 @@ function onBottom(event) {
* msgMoveMotion.Up, msgMoveMotion.Down, msgMoveMotion.Top, msgMoveMotion.Bottom
*/
function moveFilter(motion) {
// At the moment, do not allow moving groups of filters.
let selectedFilter = currentFilter();
if (!selectedFilter)
return;
- let filterList = currentFilterList();
let moveFilterNative;
switch (motion) {
case msgMoveMotion.Top:
- filterList.removeFilter(selectedFilter);
- filterList.insertFilterAt(0, selectedFilter);
- gFilterTree.treeBoxObject.ensureRowIsVisible(0);
- gFilterTree.view.selection.select(0);
+ if (selectedFilter) {
+ gCurrentFilterList.removeFilter(selectedFilter);
+ gCurrentFilterList.insertFilterAt(0, selectedFilter);
+ rebuildFilterList();
+ }
return;
case msgMoveMotion.Bottom:
- filterList.removeFilter(selectedFilter);
- filterList.insertFilterAt(filterList.filterCount, selectedFilter);
- gFilterTree.treeBoxObject.ensureRowIsVisible(filterList.filterCount - 1);
- gFilterTree.view.selection.select(filterList.filterCount - 1);
+ if (selectedFilter) {
+ gCurrentFilterList.removeFilter(selectedFilter);
+ gCurrentFilterList.insertFilterAt(gCurrentFilterList.filterCount,
+ selectedFilter);
+ rebuildFilterList();
+ }
return;
case msgMoveMotion.Up:
moveFilterNative = Ci.nsMsgFilterMotion.up;
break;
case msgMoveMotion.Down:
moveFilterNative = Ci.nsMsgFilterMotion.down;
break;
}
moveCurrentFilter(moveFilterNative);
}
function viewLog()
{
- var filterList = currentFilterList();
- var args = {filterList: filterList};
+ let args = {filterList: gCurrentFilterList};
window.openDialog("chrome://messenger/content/viewLog.xul", "FilterLog", "chrome,modal,titlebar,resizable,centerscreen", args);
}
function onFilterUnload()
{
// make sure to save the filter to disk
- var filterList = currentFilterList();
- if (filterList)
- filterList.saveToDefaultFile();
+ if (gCurrentFilterList)
+ gCurrentFilterList.saveToDefaultFile();
Services.obs.removeObserver(onFilterClose, "quit-application-requested");
top.controllers.removeController(gFilterController);
}
function onFilterClose(aCancelQuit, aTopic, aData)
{
if (aTopic == "quit-application-requested" &&
@@ -591,58 +546,150 @@ function runSelectedFilters()
let folder = gRunFiltersFolder._folder ||
gRunFiltersFolder.selectedItem._folder;
if (!folder)
return;
let filterList = MailServices.filters.getTempFilterList(folder);
// make sure the tmp filter list uses the real filter list log stream
- filterList.loggingEnabled = currentFilterList().loggingEnabled;
- filterList.logStream = currentFilterList().logStream;
- var index = 0, sel = gFilterTree.view.selection;
- for (var i = 0; i < sel.getRangeCount(); i++) {
- var start = {}, end = {};
- sel.getRangeAt(i, start, end);
- for (var j = start.value; j <= end.value; j++) {
- var curFilter = getFilter(j);
- if (curFilter)
- filterList.insertFilterAt(index++, curFilter);
- }
+ filterList.logStream = gCurrentFilterList.logStream;
+ filterList.loggingEnabled = gCurrentFilterList.loggingEnabled;
+
+ let index = 0;
+ for (let item of gFilterListbox.selectedItems) {
+ filterList.insertFilterAt(index++, item._filter);
}
MailServices.filters.applyFiltersToFolders(filterList, [folder], gFilterListMsgWindow);
}
function moveCurrentFilter(motion)
{
- var filterList = currentFilterList();
- var filter = currentFilter();
- if (!filterList || !filter)
- return;
+ let filter = currentFilter();
+ if (!filter)
+ return;
- filterList.moveFilter(filter, motion);
- if (motion == Ci.nsMsgFilterMotion.up)
- gFilterTree.view.selection.select(gFilterTree.currentIndex - 1);
- else
- gFilterTree.view.selection.select(gFilterTree.currentIndex + 1);
-
- gFilterTree.treeBoxObject.ensureRowIsVisible(gFilterTree.currentIndex);
+ gCurrentFilterList.moveFilter(filter, motion);
+ rebuildFilterList();
}
/**
+ * Redraws the list of filters. Takes the search box value into account.
+ *
+ * This function should perform very fast even in case of high number of filters.
+ * Therefore there are some optimizations (e.g. listelement.children[] instead of
+ * list.getItemAtIndex()), that favour speed vs. semantical perfection.
+ */
+function rebuildFilterList()
+{
+ // Make a note of which filters were previously selected
+ let selectedNames = [];
+ for (let i = 0; i < gFilterListbox.selectedItems.length; i++)
+ selectedNames.push(gFilterListbox.selectedItems[i]._filter.filterName);
+
+ // Save scroll position so we can try to restore it later.
+ // Doesn't work when the list is rebuilt after search box condition changed.
+ let firstVisibleRowIndex = gFilterListbox.getIndexOfFirstVisibleRow();
+
+ // listbox.xml seems to cache the value of the first selected item in a
+ // range at _selectionStart. The old value though is now obsolete,
+ // since we will recreate all of the elements. We need to clear this,
+ // and one way to do this is with a call to clearSelection. This might be
+ // ugly from an accessibility perspective, since it fires an onSelect event.
+ gFilterListbox.clearSelection();
+
+ let listitem, nameCell, enabledCell, filter;
+ let filterCount = gCurrentFilterList.filterCount;
+ let listitemCount = gFilterListbox.itemCount;
+ let listitemIndex = 0;
+ for (let i = 0; i < filterCount; i++) {
+ filter = gCurrentFilterList.getFilterAt(i);
+
+ if (listitemCount > listitemIndex) {
+ // If there is a free existing listitem, reuse it.
+ // Use .children[] instead of .getItemAtIndex() as it is much faster.
+ listitem = gFilterListbox.children[listitemIndex + 1];
+ nameCell = listitem.childNodes[0];
+ enabledCell = listitem.childNodes[1];
+ }
+ else
+ {
+ // If there are not enough listitems in the list, create a new one.
+ listitem = document.createElement("listitem");
+ listitem.setAttribute("role", "checkbox");
+ nameCell = document.createElement("listcell");
+ enabledCell = document.createElement("listcell");
+ enabledCell.setAttribute("class", "listcell-iconic");
+ listitem.appendChild(nameCell);
+ listitem.appendChild(enabledCell);
+ gFilterListbox.appendChild(listitem);
+ let size = (enabledCell.clientWidth - 28) / 2;
+ enabledCell.style.paddingLeft = size.toString() + "px";
+ // We have to attach this listener to the listitem, even though we only
+ // care about clicks on the enabledCell. However, attaching to that item
+ // doesn't result in any events actually getting received.
+ listitem.addEventListener("click", onFilterClick, true);
+ listitem.addEventListener("dblclick", onFilterDoubleClick, true);
+ }
+ // For accessibility set the label on listitem.
+ listitem.setAttribute("label", filter.filterName);
+ // Set the listitem values to represent the current filter.
+ nameCell.setAttribute("label", filter.filterName);
+ enabledCell.setAttribute("enabled", filter.enabled);
+ listitem.setAttribute("aria-checked", filter.enabled);
+ listitem._filter = filter;
+
+ if (selectedNames.includes(filter.filterName))
+ gFilterListbox.addItemToSelection(listitem);
+
+ listitemIndex++;
+ }
+ // Remove any superfluous listitems, if the number of filters shrunk.
+ for (let i = listitemCount - 1; i >= listitemIndex; i--) {
+ gFilterListbox.lastChild.remove();
+ }
+
+ updateViewPosition(firstVisibleRowIndex);
+
+ gFilterListbox.focus();
+}
+
+function updateViewPosition(firstVisibleRowIndex)
+{
+ if (firstVisibleRowIndex == -1)
+ firstVisibleRowIndex = gFilterListbox.getIndexOfFirstVisibleRow();
+
+ // Restore to the extent possible the scroll position.
+ if (firstVisibleRowIndex && gFilterListbox.itemCount)
+ gFilterListbox.scrollToIndex(Math.min(firstVisibleRowIndex,
+ gFilterListbox.itemCount - 1));
+
+ if (gFilterListbox.selectedCount) {
+ // Make sure that at least the first selected item is visible.
+ gFilterListbox.ensureElementIsVisible(gFilterListbox.selectedItems[0]);
+
+ // The current item should be the first selected item, so that keyboard
+ // selection extension can work.
+ gFilterListbox.currentItem = gFilterListbox.selectedItems[0];
+ }
+
+ updateButtons();
+}
+
+/**
* Try to only enable buttons that make sense
* - moving filters is currently only enabled for single selection
* also movement is restricted by searchBox and current selection position
* - edit only for single filters
* - delete / run only for one or more selected filters
*/
function updateButtons()
{
- var numFiltersSelected = gFilterTree.view.selection.count;
+ var numFiltersSelected = gFilterListbox.selectedItems.length;
var oneFilterSelected = (numFiltersSelected == 1);
// "edit" only enabled when one filter selected
// or if we couldn't parse the filter.
let disabled = !oneFilterSelected || currentFilter().unparseable;
gEditButton.disabled = disabled;
// "copy" is the same as "edit".
@@ -655,24 +702,27 @@ function updateButtons()
// so only disable this UI if no filters are selected
gRunFiltersFolderPrefix.disabled = !numFiltersSelected;
gRunFiltersFolder.disabled = !numFiltersSelected;
gRunFiltersButton.disabled = !numFiltersSelected ||
!gRunFiltersFolder._folder;
// "up" and "top" enabled only if one filter is selected,
// and it's not the first.
- disabled = !(oneFilterSelected && gFilterTree.currentIndex > 0);
+ // Don't use gFilterListbox.currentIndex here, it's buggy when we've just
+ // changed the children in the list (via rebuildFilterList)
+ disabled = !(oneFilterSelected &&
+ gFilterListbox.getSelectedItem(0) != gFilterListbox.getItemAtIndex(0));
gUpButton.disabled = disabled;
gTopButton.disabled = disabled;
// "down" and "bottom" enabled only if one filter selected,
// and it's not the last.
disabled = !(oneFilterSelected &&
- gFilterTree.currentIndex < gFilterTree.view.rowCount - 1);
+ gFilterListbox.selectedIndex < gFilterListbox.itemCount - 1);
gDownButton.disabled = disabled;
gBottomButton.disabled = disabled;
}
/**
* Given a selected folder, returns the folder where filters should
* be defined (the root folder except for news) if the server can
* accept filters.
@@ -714,35 +764,42 @@ function getServerThatCanHaveFilters()
{
if (currentServer.canHaveFilters)
return currentServer;
}
return null;
}
+function onFilterClick(event)
+{
+ // We only care about button 0 (left click) events.
+ if (event.button != 0)
+ return;
+
+ // Remember, we had to attach the click-listener to the whole listitem, so
+ // now we need to see if the clicked the enable-column
+ let toggle = event.target.childNodes[1];
+ if ((event.clientX < toggle.boxObject.x + toggle.boxObject.width) &&
+ (event.clientX > toggle.boxObject.x)) {
+ toggleFilter(event.target);
+ event.stopPropagation();
+ }
+}
+
function onFilterDoubleClick(event)
{
- // we only care about button 0 (left click) events
- if (event.button != 0)
- return;
+ // We only care about button 0 (left click) events.
+ if (event.button != 0)
+ return;
- var cell = gFilterTree.treeBoxObject.getCellAt(event.clientX, event.clientY);
- if (cell.row == -1 || cell.row > gFilterTree.view.rowCount - 1 || event.originalTarget.localName != "treechildren") {
- // double clicking on a non valid row should not open the edit filter dialog
- return;
- }
-
- // if the cell is in a "cycler" column (the enabled column)
- // don't open the edit filter dialog with the selected filter
- if (!cell.col.cycler)
- onEditFilter();
+ onEditFilter();
}
-function onFilterTreeKeyPress(aEvent) {
+function onFilterListKeyPress(aEvent) {
if (aEvent.ctrlKey || aEvent.altKey || aEvent.metaKey || aEvent.shiftKey)
return;
if (aEvent.keyCode) {
switch (aEvent.keyCode) {
case KeyEvent.DOM_VK_INSERT:
if (!gNewButton.disabled)
onNewFilter();
@@ -756,24 +813,19 @@ function onFilterTreeKeyPress(aEvent) {
onEditFilter();
break;
}
return;
}
switch (aEvent.charCode) {
case KeyEvent.DOM_VK_SPACE:
- let rangeCount = gFilterTree.view.selection.getRangeCount();
- for (let i = 0; i < rangeCount; ++i) {
- let start = {}, end = {};
- gFilterTree.view.selection.getRangeAt(i, start, end);
- for (let k = start.value; k <= end.value; ++k)
- toggleFilter(k);
+ for (let item of gFilterListbox.selectedItems) {
+ toggleFilter(item);
}
- gFilterTree.view.selection.invalidateSelection();
break;
default:
}
}
function doHelpButton()
{
openHelp("mail-filters");
@@ -789,15 +841,15 @@ var gFilterController =
isCommandEnabled: function(aCommand)
{
return aCommand == "cmd_selectAll";
},
doCommand: function(aCommand)
{
if (aCommand == "cmd_selectAll")
- gFilterTree.view.selection.selectAll();
+ gFilterListbox.selectAll();
},
onEvent: function(aEvent)
{
}
};