/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ const {PluralForm} = ChromeUtils.import("resource://gre/modules/PluralForm.jsm"); var gSearchSession = null; var gPreQuickSearchView = null; var gSearchTimer = null; var gViewSearchListener; var gSearchBundle; var gProgressMeter = null; var gSearchInProgress = false; var gClearButton = null; var gDefaultSearchViewTerms = null; var gQSViewIsDirty = false; var gNumTotalMessages; var gNumUnreadMessages; function SetQSStatusText(aNumHits) { var statusMsg; // if there are no hits, it means no matches were found in the search. if (aNumHits == 0) { statusMsg = gSearchBundle.getString("noMatchesFound"); } else { statusMsg = PluralForm.get(aNumHits, gSearchBundle.getString("matchesFound")); statusMsg = statusMsg.replace("#1", aNumHits); } statusFeedback.showStatusString(statusMsg); } // nsIMsgSearchNotify object var gSearchNotificationListener = { onSearchHit: function(header, folder) { gNumTotalMessages++; if (!header.isRead) gNumUnreadMessages++; // XXX todo // update status text? }, onSearchDone: function(status) { SetQSStatusText(gDBView.QueryInterface(Ci.nsITreeView).rowCount) statusFeedback.showProgress(0); gProgressMeter.setAttribute("mode", "normal"); gSearchInProgress = false; // ### TODO need to find out if there's quick search within a virtual folder. if (gCurrentVirtualFolderUri && (!gSearchInput || gSearchInput.value == "")) { var vFolder = MailUtils.getFolderForURI(gCurrentVirtualFolderUri, false); var dbFolderInfo = vFolder.msgDatabase.dBFolderInfo; dbFolderInfo.numUnreadMessages = gNumUnreadMessages; dbFolderInfo.numMessages = gNumTotalMessages; vFolder.updateSummaryTotals(true); // force update from db. var msgdb = vFolder.msgDatabase; msgdb.Commit(Ci.nsMsgDBCommitType.kLargeCommit); // now that we have finished loading a virtual folder, // scroll to the correct message if there is at least one. if (vFolder.getTotalMessages(false) > 0) ScrollToMessageAfterFolderLoad(vFolder); } }, onNewSearch: function() { statusFeedback.showProgress(0); statusFeedback.showStatusString(gSearchBundle.getString("searchingMessage")); gProgressMeter.setAttribute("mode", "undetermined"); gSearchInProgress = true; gNumTotalMessages = 0; gNumUnreadMessages = 0; } } function getDocumentElements() { gSearchBundle = document.getElementById("bundle_search"); gProgressMeter = document.getElementById('statusbar-icon'); gClearButton = document.getElementById('clearButton'); GetSearchInput(); } function addListeners() { gViewSearchListener = gDBView.QueryInterface(Ci.nsIMsgSearchNotify); gSearchSession.registerListener(gViewSearchListener); } function removeListeners() { gSearchSession.unregisterListener(gViewSearchListener); } function removeGlobalListeners() { removeListeners(); gSearchSession.unregisterListener(gSearchNotificationListener); } function initializeGlobalListeners() { // Setup the javascript object as a listener on the search results gSearchSession.registerListener(gSearchNotificationListener); } function createQuickSearchView() { //if not already in quick search view if (gDBView.viewType != nsMsgViewType.eShowQuickSearchResults) { var treeView = gDBView.QueryInterface(Ci.nsITreeView); //clear selection if (treeView && treeView.selection) treeView.selection.clearSelection(); gPreQuickSearchView = gDBView; if (gDBView.viewType == nsMsgViewType.eShowVirtualFolderResults) { // remove the view as a listener on the search results var saveViewSearchListener = gDBView.QueryInterface(Ci.nsIMsgSearchNotify); gSearchSession.unregisterListener(saveViewSearchListener); } CreateDBView(gDBView.msgFolder, (gXFVirtualFolderTerms) ? nsMsgViewType.eShowVirtualFolderResults : nsMsgViewType.eShowQuickSearchResults, gDBView.viewFlags, gDBView.sortType, gDBView.sortOrder); } } function initializeSearchBar() { createQuickSearchView(); if (!gSearchSession) { var searchSessionContractID = "@mozilla.org/messenger/searchSession;1"; gSearchSession = Cc[searchSessionContractID].createInstance(Ci.nsIMsgSearchSession); initializeGlobalListeners(); } else { if (gSearchInProgress) { onSearchStop(); gSearchInProgress = false; } removeListeners(); } addListeners(); } function onEnterInSearchBar() { if (!gSearchBundle) getDocumentElements(); if (gSearchInput.value == "") { let viewType = gDBView && gDBView.viewType; if (viewType == nsMsgViewType.eShowQuickSearchResults || viewType == nsMsgViewType.eShowVirtualFolderResults) { statusFeedback.showStatusString(""); disableQuickSearchClearButton(); viewDebug ("onEnterInSearchBar gDefaultSearchViewTerms = " + gDefaultSearchViewTerms + "gVirtualFolderTerms = " + gVirtualFolderTerms + "gXFVirtualFolderTerms = " + gXFVirtualFolderTerms + "\n"); var addTerms = gDefaultSearchViewTerms || gVirtualFolderTerms || gXFVirtualFolderTerms; if (addTerms) { viewDebug ("addTerms = " + addTerms + " count = " + addTerms.length + "\n"); initializeSearchBar(); onSearch(addTerms); } else restorePreSearchView(); } else if (gPreQuickSearchView && !gDefaultSearchViewTerms)// may be a quick search from a cross-folder virtual folder restorePreSearchView(); gQSViewIsDirty = false; return; } initializeSearchBar(); if (gClearButton) gClearButton.setAttribute("disabled", false); //coming into search enable clear button ClearThreadPaneSelection(); ClearMessagePane(); onSearch(null); gQSViewIsDirty = false; } function restorePreSearchView() { var selectedHdr = null; //save selection try { selectedHdr = gDBView.hdrForFirstSelectedMessage; } catch (ex) {} //we might have to sort the view coming out of quick search var sortType = gDBView.sortType; var sortOrder = gDBView.sortOrder; var viewFlags = gDBView.viewFlags; var folder = gDBView.msgFolder; gDBView.close(); gDBView = null; if (gPreQuickSearchView) { gDBView = gPreQuickSearchView; if (gDBView.viewType == nsMsgViewType.eShowVirtualFolderResults) { // readd the view as a listener on the search results var saveViewSearchListener = gDBView.QueryInterface(Ci.nsIMsgSearchNotify); if (gSearchSession) gSearchSession.registerListener(saveViewSearchListener); } // dump ("view type = " + gDBView.viewType + "\n"); if (sortType != gDBView.sortType || sortOrder != gDBView.sortOrder) { gDBView.sort(sortType, sortOrder); } UpdateSortIndicators(sortType, sortOrder); gPreQuickSearchView = null; } else //create default view type CreateDBView(folder, nsMsgViewType.eShowAllThreads, viewFlags, sortType, sortOrder); RerootThreadPane(); var scrolled = false; // now restore selection if (selectedHdr) { gDBView.selectMsgByKey(selectedHdr.messageKey); var treeView = gDBView.QueryInterface(Ci.nsITreeView); var selectedIndex = treeView.selection.currentIndex; if (selectedIndex >= 0) { // scroll EnsureRowInThreadTreeIsVisible(selectedIndex); scrolled = true; } else ClearMessagePane(); } if (!scrolled) ScrollToMessageAfterFolderLoad(null); } function onSearch(aSearchTerms) { viewDebug("in OnSearch, searchTerms = " + aSearchTerms + "\n"); RerootThreadPane(); if (aSearchTerms) createSearchTermsWithList(aSearchTerms); else createSearchTerms(); gDBView.searchSession = gSearchSession; try { gSearchSession.search(msgWindow); } catch(ex) { dump("Search Exception\n"); } } function createSearchTermsWithList(aTermsArray) { var nsMsgSearchScope = Ci.nsMsgSearchScope; var nsMsgSearchAttrib = Ci.nsMsgSearchAttrib; var nsMsgSearchOp = Ci.nsMsgSearchOp; gSearchSession.searchTerms.clear(); gSearchSession.clearScopes(); var i; var selectedFolder = GetThreadPaneFolder(); if (gXFVirtualFolderTerms) { var msgDatabase = selectedFolder.msgDatabase; if (msgDatabase) { var dbFolderInfo = msgDatabase.dBFolderInfo; var srchFolderUri = dbFolderInfo.getCharProperty("searchFolderUri"); viewDebug("createSearchTermsWithList xf vf scope = " + srchFolderUri + "\n"); var srchFolderUriArray = srchFolderUri.split('|'); for (i in srchFolderUriArray) { let realFolder = MailUtils.getFolderForURI(srchFolderUriArray[i]); if (!realFolder.isServer) gSearchSession.addScopeTerm(nsMsgSearchScope.offlineMail, realFolder); } } } else { viewDebug ("in createSearchTermsWithList, adding scope term for selected folder\n"); gSearchSession.addScopeTerm(nsMsgSearchScope.offlineMail, selectedFolder); } // Add each item in aTermsArray to the search session. for (let term of aTermsArray) { gSearchSession.appendTerm(term); } } function createSearchTerms() { var nsMsgSearchScope = Ci.nsMsgSearchScope; var nsMsgSearchAttrib = Ci.nsMsgSearchAttrib; var nsMsgSearchOp = Ci.nsMsgSearchOp; // create an nsIMutableArray to store our search terms var searchTermsArray = Cc["@mozilla.org/array;1"] .createInstance(Ci.nsIMutableArray); var selectedFolder = GetThreadPaneFolder(); // implement | for QS // does this break if the user types "foo|bar" expecting to see subjects with that string? // I claim no, since "foo|bar" will be a hit for "foo" || "bar" // they just might get more false positives var termList = gSearchInput.value.split("|"); for (var i = 0; i < termList.length; i ++) { // if the term is empty, skip it if (termList[i] == "") continue; // create, fill, and append the subject term var term = gSearchSession.createTerm(); var value = term.value; value.str = termList[i]; term.value = value; term.attrib = nsMsgSearchAttrib.Subject; term.op = nsMsgSearchOp.Contains; term.booleanAnd = false; searchTermsArray.appendElement(term); // create, fill, and append the AllAddresses term term = gSearchSession.createTerm(); value = term.value; value.str = termList[i]; term.value = value; term.attrib = nsMsgSearchAttrib.AllAddresses; term.op = nsMsgSearchOp.Contains; term.booleanAnd = false; searchTermsArray.appendElement(term); } // now append the default view or virtual folder criteria to the quick search // so we don't lose any default view information viewDebug("gDefaultSearchViewTerms = " + gDefaultSearchViewTerms + "gVirtualFolderTerms = " + gVirtualFolderTerms + "gXFVirtualFolderTerms = " + gXFVirtualFolderTerms + "\n"); var defaultSearchTerms = (gDefaultSearchViewTerms || gVirtualFolderTerms || gXFVirtualFolderTerms); if (defaultSearchTerms) { for (let searchTerm of defaultSearchTerms) { searchTermsArray.appendElement(searchTerm); } } createSearchTermsWithList(searchTermsArray); // now that we've added the terms, clear out our input array searchTermsArray.clear(); } function onSearchStop() { gSearchSession.interruptSearch(); } function onClearSearch() { // Use the last focused element so that focus can be restored // if it does not exist, try and get the thread tree instead var focusedElement = gLastFocusedElement || GetThreadTree(); Search(""); focusedElement.focus(); } function disableQuickSearchClearButton() { if (gClearButton) gClearButton.setAttribute("disabled", true); //going out of search disable clear button } function ClearQSIfNecessary() { GetSearchInput(); if (gSearchInput.value == "") return; Search(""); } function Search(str) { GetSearchInput(); if (str != gSearchInput.value) { gQSViewIsDirty = true; viewDebug("in Search(), setting gQSViewIsDirty true\n"); } gSearchInput.value = str; //on input does not get fired for some reason onEnterInSearchBar(); }