# -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (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.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998-1999 # the Initial Developer. All Rights Reserved. # # Contributor(s): # Jan Varga # Håkan Waara (hwaara@chello.se) # David Bienvenu (bienvenu@nventure.com) # Jeremy Morton (bugzilla@game-point.net) # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** /* * Command-specific code. This stuff should be called by the widgets */ //NOTE: gMessengerBundle and gBrandBundle must be defined and set // for this Overlay to work properly var gFolderJustSwitched = false; var gVirtualFolderTerms; var gXFVirtualFolderTerms; var gCurrentVirtualFolderUri; var gPrevFolderFlags; var gPrevSelectedFolder; var gMsgFolderSelected; /* keep in sync with nsMsgFolderFlags.h */ var MSG_FOLDER_FLAG_MAIL = 0x0004; var MSG_FOLDER_FLAG_VIRTUAL = 0x0020; var MSG_FOLDER_FLAG_TRASH = 0x0100; var MSG_FOLDER_FLAG_SENTMAIL = 0x0200; var MSG_FOLDER_FLAG_DRAFTS = 0x0400; var MSG_FOLDER_FLAG_QUEUE = 0x0800; var MSG_FOLDER_FLAG_INBOX = 0x1000; var MSG_FOLDER_FLAG_TEMPLATES = 0x400000; var MSG_FOLDER_FLAG_JUNK = 0x40000000; var MSG_FOLDER_FLAG_FAVORITE = 0x80000000; function GetMsgFolderFromResource(folderResource) { if (!folderResource) return null; var msgFolder = folderResource.QueryInterface(Components.interfaces.nsIMsgFolder); if (msgFolder && (msgFolder.parent || msgFolder.isServer)) return msgFolder; else return null; } function GetServer(uri) { if (!uri) return null; try { var folder = GetMsgFolderFromUri(uri, true); return folder.server; } catch (ex) { dump("GetServer("+uri+") failed, ex="+ex+"\n"); } return null; } function setTitleFromFolder(msgfolder, subject) { var wintype = document.documentElement.getAttribute('windowtype'); var title; // If we are showing the mail:3pane. Never include the subject of the selected // message in the title. ("Inbox - My Mail - Mozilla Thunderbird") // If we are a stand alone message window, we should show the Subject // and the product but not the account name: "Re: New window Title - Mozilla Thunderbird" if (wintype == "mail:messageWindow") title = subject ? subject : ""; else if (msgfolder) { title = msgfolder.prettyName; if (!msgfolder.isServer) { var server = msgfolder.server; var middle; var end; if (server.type == "nntp") { // on middle = gMessengerBundle.getString("titleNewsPreHost"); end = server.hostName; } else { var identity; try { var identities = accountManager.GetIdentitiesForServer(server); identity = identities.QueryElementAt(0, Components.interfaces.nsIMsgIdentity); // - middle = "-"; end = server.prettyName; } catch (ex) {} } if (middle) title += " " + middle; if (end) title += " " + end; } } #ifndef XP_MACOSX title += " - " + gBrandBundle.getString("brandFullName"); #endif document.title = title; } function UpdateMailToolbar(caller) { //dump("XXX update mail-toolbar " + caller + "\n"); document.commandDispatcher.updateCommands('mail-toolbar'); // hook for extra toolbar items var observerService = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService); observerService.notifyObservers(window, "mail:updateToolbarItems", null); } /** * @param folder - If viewFolder is a single folder saved - search, this folder is the scope of the - saved search, the real, underlying folder. - Otherwise, it's the same as the viewFolder. * @param viewFolder - nsIMsgFolder selected in the folder pane. - Will be the same as folder, except if - it's a single folder saved search. * @param viewType - nsMsgViewType (see nsIMsgDBView.idl) * @param viewFlags - nsMsgViewFlagsType (see nsIMsgDBView.idl) * @param sortType - nsMsgViewSortType (see nsIMsgDBView.idl) * @param sortOrder - nsMsgViewSortOrder (see nsIMsgDBView.idl) **/ function ChangeFolder(folder, viewFolder, viewType, viewFlags, sortType, sortOrder) { if (folder.URI == gCurrentLoadingFolderURI) return; // hook for extra toolbar items var observerService = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService); observerService.notifyObservers(window, "mail:setupToolbarItems", folder.URI); try { setTitleFromFolder(viewFolder, null); } catch (ex) { dump("error setting title: " + ex + "\n"); } //if it's a server, clear the threadpane and don't bother trying to load. if(folder.isServer) { msgWindow.openFolder = null; ClearThreadPane(); UpdateStatusQuota(null); // Load AccountCentral page here. ShowAccountCentral(); return; } else { if (folder.server.displayStartupPage) { gDisplayStartupPage = true; folder.server.displayStartupPage = false; } } // If the user clicks on msgfolder, time to display thread pane and message pane. ShowThreadPane(); gCurrentLoadingFolderURI = folder.URI; gNextMessageAfterDelete = null; // forget what message to select, if any gCurrentFolderToReroot = folder.URI; gCurrentLoadingFolderViewFlags = viewFlags; gCurrentLoadingFolderViewType = viewType; gCurrentLoadingFolderSortType = sortType; gCurrentLoadingFolderSortOrder = sortOrder; var showMessagesAfterLoading; try { var server = folder.server; if (gPrefBranch.getBoolPref("mail.password_protect_local_cache")) { showMessagesAfterLoading = server.passwordPromptRequired; // servers w/o passwords (like local mail) will always be non-authenticated. // So we need to use the account manager for that case. } else showMessagesAfterLoading = false; } catch (ex) { showMessagesAfterLoading = false; } if (viewType != nsMsgViewType.eShowVirtualFolderResults && (folder.manyHeadersToDownload || showMessagesAfterLoading)) { gRerootOnFolderLoad = true; try { ClearThreadPane(); SetBusyCursor(window, true); folder.startFolderLoading(); folder.updateFolder(msgWindow); } catch(ex) { SetBusyCursor(window, false); dump("Error loading with many headers to download: " + ex + "\n"); } } else { if (viewType != nsMsgViewType.eShowVirtualFolderResults) SetBusyCursor(window, true); RerootFolder(folder.URI, folder, viewType, viewFlags, sortType, sortOrder); gRerootOnFolderLoad = false; folder.startFolderLoading(); //Need to do this after rerooting folder. Otherwise possibility of receiving folder loaded //notification before folder has actually changed. if (viewType != nsMsgViewType.eShowVirtualFolderResults) folder.updateFolder(msgWindow); } } function isNewsURI(uri) { if (!uri || uri[0] != 'n') { return false; } else { return ((uri.substring(0,6) == "news:/") || (uri.substring(0,14) == "news-message:/")); } } function RerootFolder(uri, newFolder, viewType, viewFlags, sortType, sortOrder) { viewDebug("In reroot folder, sortType = " + sortType + "viewType = " + viewType + "\n"); if (sortType == 0) { try { var msgdb = newFolder.getMsgDatabase(msgWindow); var dbFolderInfo = msgdb.dBFolderInfo; sortType = dbFolderInfo.sortType; sortOrder = dbFolderInfo.sortOrder; viewFlags = dbFolderInfo.viewFlags; viewType = dbFolderInfo.viewType; dbFolderInfo = null; } catch(ex) { dump("invalid db in RerootFolder: " + ex + "\n"); } } // workaround for #39655 gFolderJustSwitched = true; ClearThreadPaneSelection(); //Clear the new messages of the old folder var oldFolder = gPrevSelectedFolder; if (oldFolder) { oldFolder.clearNewMessages(); oldFolder.hasNewMessages = false; } //Set the window's new open folder. msgWindow.openFolder = newFolder; //the new folder being selected should have its biff state get cleared. if(newFolder) { newFolder.biffState = Components.interfaces.nsIMsgFolder.nsMsgBiffState_NoMail; } //Clear out the thread pane so that we can sort it with the new sort id without taking any time. // folder.setAttribute('ref', ""); // null this out, so we don't try sort. if (gDBView) { gDBView.close(); gDBView = null; } // cancel the pending mark as read timer ClearPendingReadTimer(); // if this is the drafts, sent, or send later folder, // we show "Recipient" instead of "Author" SetSentFolderColumns(IsSpecialFolder(newFolder, MSG_FOLDER_FLAG_SENTMAIL | MSG_FOLDER_FLAG_DRAFTS | MSG_FOLDER_FLAG_QUEUE, true)); ShowLocationColumn(viewType == nsMsgViewType.eShowVirtualFolderResults); // Only show 'Received' column for e-mails. For newsgroup messages, the 'Date' header is as reliable as an e-mail's // 'Received' header, as it is replaced with the news server's (more reliable) date. UpdateReceivedColumn(newFolder); // now create the db view, which will sort it. CreateDBView(newFolder, viewType, viewFlags, sortType, sortOrder); if (oldFolder) { /* we don't null out the db reference for inbox because inbox is like the "main" folder and performance outweighs footprint*/ if (!IsSpecialFolder(oldFolder, MSG_FOLDER_FLAG_INBOX, false)) { if (oldFolder.URI != newFolder.URI) oldFolder.setMsgDatabase(null); } } // that should have initialized gDBView, now re-root the thread pane RerootThreadPane(); SetUpToolbarButtons(uri); UpdateFolderLocationPicker(gMsgFolderSelected); UpdateStatusMessageCounts(gMsgFolderSelected); // hook for extra toolbar items var observerService = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService); observerService.notifyObservers(window, "mail:updateToolbarItems", null); // this is to kick off cross-folder searches for virtual folders. if (gSearchSession && !gVirtualFolderTerms) // another var might be better... { viewDebug("doing a xf folder search in rerootFolder\n"); gCurrentLoadingFolderURI = "" ViewChangeByFolder(newFolder); gPreQuickSearchView = null; // don't remember the cross folder search ScrollToMessageAfterFolderLoad(newFolder); } } function SwitchView(command) { // when switching thread views, we might be coming out of quick search // or a message view. // first set view picker to all ViewChangeByValue(kViewItemAll); // clear the QS text, if we need to ClearQSIfNecessary(); // now switch views var oldSortType = gDBView ? gDBView.sortType : nsMsgViewSortType.byThread; var oldSortOrder = gDBView ? gDBView.sortOrder : nsMsgViewSortOrder.ascending; var viewFlags = gDBView ? gDBView.viewFlags : gCurViewFlags; // close existing view. if (gDBView) { gDBView.close(); gDBView = null; } switch(command) { // "All" threads and "Unread" threads don't change threading state case "cmd_viewAllMsgs": viewFlags = viewFlags & ~nsMsgViewFlagsType.kUnreadOnly; CreateDBView(msgWindow.openFolder, nsMsgViewType.eShowAllThreads, viewFlags, oldSortType, oldSortOrder); break; case "cmd_viewUnreadMsgs": viewFlags = viewFlags | nsMsgViewFlagsType.kUnreadOnly; CreateDBView(msgWindow.openFolder, nsMsgViewType.eShowAllThreads, viewFlags, oldSortType, oldSortOrder ); break; // "Threads with Unread" and "Watched Threads with Unread" force threading case "cmd_viewThreadsWithUnread": CreateDBView(msgWindow.openFolder, nsMsgViewType.eShowThreadsWithUnread, nsMsgViewFlagsType.kThreadedDisplay, oldSortType, oldSortOrder); break; case "cmd_viewWatchedThreadsWithUnread": CreateDBView(msgWindow.openFolder, nsMsgViewType.eShowWatchedThreadsWithUnread, nsMsgViewFlagsType.kThreadedDisplay, oldSortType, oldSortOrder); break; // "Ignored Threads" toggles 'ignored' inclusion -- // but it also resets 'With Unread' views to 'All' case "cmd_viewIgnoredThreads": if (viewFlags & nsMsgViewFlagsType.kShowIgnored) viewFlags = viewFlags & ~nsMsgViewFlagsType.kShowIgnored; else viewFlags = viewFlags | nsMsgViewFlagsType.kShowIgnored; CreateDBView(msgWindow.openFolder, nsMsgViewType.eShowAllThreads, viewFlags, oldSortType, oldSortOrder); break; } RerootThreadPane(); } function SetSentFolderColumns(isSentFolder) { var tree = GetThreadTree(); var lastFolderSent = tree.getAttribute("lastfoldersent") == "true"; if (isSentFolder != lastFolderSent) { var senderColumn = document.getElementById("senderCol"); var recipientColumn = document.getElementById("recipientCol"); var saveHidden = senderColumn.getAttribute("hidden"); senderColumn.setAttribute("hidden", senderColumn.getAttribute("swappedhidden")); senderColumn.setAttribute("swappedhidden", saveHidden); saveHidden = recipientColumn.getAttribute("hidden"); recipientColumn.setAttribute("hidden", recipientColumn.getAttribute("swappedhidden")); recipientColumn.setAttribute("swappedhidden", saveHidden); } if(isSentFolder) tree.setAttribute("lastfoldersent", "true"); else tree.setAttribute("lastfoldersent", "false"); } function ShowLocationColumn(show) { var col = document.getElementById("locationCol"); if (col) { if (show) { col.removeAttribute("hidden"); col.removeAttribute("ignoreincolumnpicker"); } else { col.setAttribute("hidden","true"); col.setAttribute("ignoreincolumnpicker","true"); } } } function UpdateReceivedColumn(newFolder) { // Only show 'Received' column for e-mails. For newsgroup messages, the 'Date' header is as reliable as an e-mail's // 'Received' header, as it is replaced with the news server's (more reliable) date. var receivedColumn = document.getElementById("receivedCol"); var newFolderShowsRcvd = (newFolder.flags & MSG_FOLDER_FLAG_MAIL) && !(newFolder.flags & (MSG_FOLDER_FLAG_QUEUE | MSG_FOLDER_FLAG_DRAFTS | MSG_FOLDER_FLAG_TEMPLATES | MSG_FOLDER_FLAG_SENTMAIL)); var tempHidden = receivedColumn.getAttribute("temphidden") == "true"; var isHidden = receivedColumn.getAttribute("hidden") == "true"; if (!newFolderShowsRcvd && !isHidden) { // Record state & hide receivedColumn.setAttribute("temphidden", "true"); receivedColumn.setAttribute("hidden", "true"); } else if (newFolderShowsRcvd && tempHidden && isHidden) { receivedColumn.setAttribute("hidden", "false"); } if (newFolderShowsRcvd) { receivedColumn.removeAttribute("ignoreincolumnpicker"); receivedColumn.removeAttribute("temphidden"); } else receivedColumn.setAttribute("ignoreincolumnpicker", "true"); } function SetNewsFolderColumns() { var sizeColumn = document.getElementById("sizeCol"); if (gDBView.usingLines) { sizeColumn.setAttribute("label",gMessengerBundle.getString("linesColumnHeader")); } else { sizeColumn.setAttribute("label", gMessengerBundle.getString("sizeColumnHeader")); } } function UpdateStatusMessageCounts(folder) { var unreadElement = GetUnreadCountElement(); var totalElement = GetTotalCountElement(); if(folder && unreadElement && totalElement) { var numSelected = GetNumSelectedMessages(); var numUnread = (numSelected > 1) ? gMessengerBundle.getFormattedString("selectedMsgStatus", [numSelected]) : gMessengerBundle.getFormattedString("unreadMsgStatus", [ folder.getNumUnread(false)]); var numTotal = gMessengerBundle.getFormattedString("totalMsgStatus", [folder.getTotalMessages(false)]); unreadElement.setAttribute("label", numUnread); totalElement.setAttribute("label", numTotal); unreadElement.hidden = false; totalElement.hidden = false; } } var gQuotaUICache; function UpdateStatusQuota(folder) { if (!(folder && // no folder selected folder instanceof Components.interfaces.nsIMsgImapMailFolder)) // POP etc. { if (typeof(gQuotaUICache) == "object") // ever shown quota gQuotaUICache.panel.hidden = true; return; } folder = folder.QueryInterface(Components.interfaces.nsIMsgImapMailFolder); // get element references and prefs if (typeof(gQuotaUICache) != "object") { gQuotaUICache = new Object(); gQuotaUICache.meter = document.getElementById("quotaMeter"); gQuotaUICache.panel = document.getElementById("quotaPanel"); gQuotaUICache.label = document.getElementById("quotaLabel"); const kBranch = "mail.quota.mainwindow_threshold."; gQuotaUICache.showTreshold = gPrefBranch.getIntPref(kBranch + "show"); gQuotaUICache.warningTreshold = gPrefBranch.getIntPref(kBranch + "warning"); gQuotaUICache.criticalTreshold = gPrefBranch.getIntPref(kBranch + "critical"); } var valid = {value: null}; var used = {value: null}; var max = {value: null}; try { // get data from backend folder.getQuota(valid, used, max); } catch (e) { dump(e + "\n"); } if (valid.value && max.value > 0) { var percent = Math.round(used.value / max.value * 100); // show in UI if (percent < gQuotaUICache.showTreshold) gQuotaUICache.panel.hidden = true; else { gQuotaUICache.panel.hidden = false; gQuotaUICache.meter.setAttribute("value", percent); // do not use value property, because that is imprecise (3%) // for optimization that we don't need here var label = gMessengerBundle.getFormattedString("percent", [percent]); var tooltip = gMessengerBundle.getFormattedString("quotaTooltip", [used.value, max.value]); gQuotaUICache.label.value = label; gQuotaUICache.label.tooltipText = tooltip; if (percent < gQuotaUICache.warningTreshold) gQuotaUICache.panel.removeAttribute("alert"); else if (percent < gQuotaUICache.criticalTreshold) gQuotaUICache.panel.setAttribute("alert", "warning"); else gQuotaUICache.panel.setAttribute("alert", "critical"); } } else gQuotaUICache.panel.hidden = true; } function ConvertColumnIDToSortType(columnID) { var sortKey; switch (columnID) { case "dateCol": sortKey = nsMsgViewSortType.byDate; break; case "receivedCol": sortKey = nsMsgViewSortType.byReceived; break; case "senderCol": sortKey = nsMsgViewSortType.byAuthor; break; case "recipientCol": sortKey = nsMsgViewSortType.byRecipient; break; case "subjectCol": sortKey = nsMsgViewSortType.bySubject; break; case "locationCol": sortKey = nsMsgViewSortType.byLocation; break; case "accountCol": sortKey = nsMsgViewSortType.byAccount; break; case "unreadButtonColHeader": sortKey = nsMsgViewSortType.byUnread; break; case "statusCol": sortKey = nsMsgViewSortType.byStatus; break; case "sizeCol": sortKey = nsMsgViewSortType.bySize; break; case "priorityCol": sortKey = nsMsgViewSortType.byPriority; break; case "flaggedCol": sortKey = nsMsgViewSortType.byFlagged; break; case "threadCol": sortKey = nsMsgViewSortType.byThread; break; case "tagsCol": sortKey = nsMsgViewSortType.byTags; break; case "junkStatusCol": sortKey = nsMsgViewSortType.byJunkStatus; break; case "idCol": sortKey = nsMsgViewSortType.byId; break; case "attachmentCol": sortKey = nsMsgViewSortType.byAttachments; break; default: //no predefined column handler - lets check if there is a custom column handler try { //try to grab the columnHandler (an error is thrown if it does not exist) columnHandler = gDBView.getColumnHandler(columnID); //it exists - save this column ID in the customSortCol property of dbFolderInfo //for later use (see nsIMsgDBView.cpp) gDBView.db.dBFolderInfo.setProperty('customSortCol', columnID); sortKey = nsMsgViewSortType.byCustom; } catch(err) { dump("unsupported sort column: " + columnID + " - no custom handler installed. (Error was: " + err + ")\n"); sortKey = 0; } break; } return sortKey; } function ConvertSortTypeToColumnID(sortKey) { var columnID; // Hack to turn this into an integer, if it was a string. // It would be a string if it came from localStore.rdf sortKey = sortKey - 0; switch (sortKey) { case nsMsgViewSortType.byDate: columnID = "dateCol"; break; case nsMsgViewSortType.byReceived: columnID = "receivedCol"; break; case nsMsgViewSortType.byAuthor: columnID = "senderCol"; break; case nsMsgViewSortType.byRecipient: columnID = "recipientCol"; break; case nsMsgViewSortType.bySubject: columnID = "subjectCol"; break; case nsMsgViewSortType.byLocation: columnID = "locationCol"; break; case nsMsgViewSortType.byAccount: columnID = "accountCol"; break; case nsMsgViewSortType.byUnread: columnID = "unreadButtonColHeader"; break; case nsMsgViewSortType.byStatus: columnID = "statusCol"; break; case nsMsgViewSortType.byTags: columnID = "tagsCol"; break; case nsMsgViewSortType.bySize: columnID = "sizeCol"; break; case nsMsgViewSortType.byPriority: columnID = "priorityCol"; break; case nsMsgViewSortType.byFlagged: columnID = "flaggedCol"; break; case nsMsgViewSortType.byThread: columnID = "threadCol"; break; case nsMsgViewSortType.byId: columnID = "idCol"; break; case nsMsgViewSortType.byJunkStatus: columnID = "junkStatusCol"; break; case nsMsgViewSortType.byAttachments: columnID = "attachmentCol"; break; case nsMsgViewSortType.byCustom: //TODO: either change try() catch to if (property exists) or restore the getColumnHandler() check try //getColumnHandler throws an errror when the ID is not handled { columnID = gDBView.db.dBFolderInfo.getProperty('customSortCol'); } catch (err) { //error - means no handler dump("ConvertSortTypeToColumnID: custom sort key but no handler for column '" + columnID + "'\n"); columnID = "dateCol"; } break; default: dump("unsupported sort key: " + sortKey + "\n"); columnID = "dateCol"; break; } return columnID; } var nsMsgViewSortType = Components.interfaces.nsMsgViewSortType; var nsMsgViewSortOrder = Components.interfaces.nsMsgViewSortOrder; var nsMsgViewFlagsType = Components.interfaces.nsMsgViewFlagsType; var nsMsgViewCommandType = Components.interfaces.nsMsgViewCommandType; var nsMsgViewType = Components.interfaces.nsMsgViewType; var nsMsgNavigationType = Components.interfaces.nsMsgNavigationType; var gDBView = null; var gCurViewFlags; var gCurSortType; // CreateDBView is called when we have a thread pane. CreateBareDBView is called when there is no // tree associated with the view. CreateDBView will call into CreateBareDBView... function CreateBareDBView(originalView, msgFolder, viewType, viewFlags, sortType, sortOrder) { var dbviewContractId = "@mozilla.org/messenger/msgdbview;1?type="; // hack to turn this into an integer, if it was a string // it would be a string if it came from localStore.rdf viewType = viewType - 0; switch (viewType) { case nsMsgViewType.eShowQuickSearchResults: dbviewContractId += "quicksearch"; break; case nsMsgViewType.eShowThreadsWithUnread: dbviewContractId += "threadswithunread"; break; case nsMsgViewType.eShowWatchedThreadsWithUnread: dbviewContractId += "watchedthreadswithunread"; break; case nsMsgViewType.eShowVirtualFolderResults: dbviewContractId += "xfvf"; break; case nsMsgViewType.eShowSearch: dbviewContractId += "search"; break; case nsMsgViewType.eShowAllThreads: default: if (sortType == nsMsgViewSortType.byThread || sortType == nsMsgViewSortType.byId || sortType == nsMsgViewSortType.byNone) viewFlags &= ~nsMsgViewFlagsType.kGroupBySort; if (viewFlags & nsMsgViewFlagsType.kGroupBySort) dbviewContractId += "group"; else dbviewContractId += "threaded"; break; } // dump ("contract id = " + dbviewContractId + "original view = " + originalView + "\n"); if (!originalView) gDBView = Components.classes[dbviewContractId].createInstance(Components.interfaces.nsIMsgDBView); gCurViewFlags = viewFlags; var count = new Object; if (!gThreadPaneCommandUpdater) gThreadPaneCommandUpdater = new nsMsgDBViewCommandUpdater(); gCurSortType = sortType; if (!originalView) { gDBView.init(messenger, msgWindow, gThreadPaneCommandUpdater); gDBView.open(msgFolder, gCurSortType, sortOrder, viewFlags, count); if (viewType == nsMsgViewType.eShowVirtualFolderResults) { // the view is a listener on the search results gViewSearchListener = gDBView.QueryInterface(Components.interfaces.nsIMsgSearchNotify); gSearchSession.registerListener(gViewSearchListener); } } else { gDBView = originalView.cloneDBView(messenger, msgWindow, gThreadPaneCommandUpdater); } } function CreateDBView(msgFolder, viewType, viewFlags, sortType, sortOrder) { // call the inner create method CreateBareDBView(null, msgFolder, viewType, viewFlags, sortType, sortOrder); // now do tree specific work // based on the collapsed state of the thread pane/message pane splitter, // suppress message display if appropriate. gDBView.suppressMsgDisplay = IsMessagePaneCollapsed(); UpdateSortIndicators(gCurSortType, sortOrder); var ObserverService = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService); ObserverService.notifyObservers(msgFolder, "MsgCreateDBView", viewType + ":" + viewFlags); } //------------------------------------------------------------ // Sets the column header sort icon based on the requested // column and direction. // // Notes: // (1) This function relies on the first part of the // matching the . The treecell // id must have a "Header" suffix. // (2) By changing the "sortDirection" attribute, a different // CSS style will be used, thus changing the icon based on // the "sortDirection" parameter. //------------------------------------------------------------ function UpdateSortIndicator(column,sortDirection) { // this is obsolete } function GetSelectedFolderResource() { var folderTree = GetFolderTree(); var startIndex = {}; var endIndex = {}; folderTree.view.selection.getRangeAt(0, startIndex, endIndex); return GetFolderResource(folderTree, startIndex.value); } function ChangeMessagePaneVisibility(now_hidden) { // we also have to hide the File/Attachments menuitem var node = document.getElementById("fileAttachmentMenu"); if (node) node.hidden = now_hidden; if (gDBView) { // the collapsed state is the state after we released the mouse // so we take it as it is gDBView.suppressMsgDisplay = now_hidden; } var event = document.createEvent('Events'); if (now_hidden) { event.initEvent('messagepane-hide', false, true); } else { event.initEvent('messagepane-unhide', false, true); } document.getElementById("messengerWindow").dispatchEvent(event); } function OnMouseUpThreadAndMessagePaneSplitter() { // the collapsed state is the state after we released the mouse // so we take it as it is ChangeMessagePaneVisibility(IsMessagePaneCollapsed()); } function FolderPaneSelectionChange() { var folderTree = GetFolderTree(); var folderSelection = folderTree.view.selection; // This prevents a folder from being loaded in the case that the user // has right-clicked on a folder different from the one that was // originally highlighted. On a right-click, the highlight (selection) // of a row will be different from the value of currentIndex, thus if // the currentIndex is not selected, it means the user right-clicked // and we don't want to load the contents of the folder. if (!folderSelection.isSelected(folderSelection.currentIndex)) return; gVirtualFolderTerms = null; gXFVirtualFolderTerms = null; if (folderSelection.count == 1) { var startIndex = {}; var endIndex = {}; folderSelection.getRangeAt(0, startIndex, endIndex); var folderResource = GetFolderResource(folderTree, startIndex.value); var uriToLoad = folderResource.Value; var msgFolder = folderResource.QueryInterface(Components.interfaces.nsIMsgFolder); if (msgFolder == gMsgFolderSelected) return; // If msgFolder turns out to be a single folder saved search, a virtual folder, // realFolder will get set to the underlying folder the // saved search is based on. var realFolder = msgFolder; gPrevSelectedFolder = gMsgFolderSelected; gMsgFolderSelected = msgFolder; UpdateFolderLocationPicker(gMsgFolderSelected); var folderFlags = msgFolder.flags; // If this is same folder, and we're not showing a virtual folder // then do nothing. if (msgFolder == msgWindow.openFolder && !(folderFlags & MSG_FOLDER_FLAG_VIRTUAL) && ! (gPrevFolderFlags & MSG_FOLDER_FLAG_VIRTUAL)) { dump("msgFolder already open" + folderResource.URI + "\n"); return; } else { OnLeavingFolder(gPrevSelectedFolder); // mark all read in last folder var sortType = 0; var sortOrder = 0; var viewFlags = 0; var viewType = 0; gDefaultSearchViewTerms = null; gVirtualFolderTerms = null; gXFVirtualFolderTerms = null; gPrevFolderFlags = folderFlags; gCurrentVirtualFolderUri = null; // don't get the db if this folder is a server // we're going to be display account central if (!(msgFolder.isServer)) { try { var msgDatabase = msgFolder.getMsgDatabase(msgWindow); if (msgDatabase) { var dbFolderInfo = msgDatabase.dBFolderInfo; sortType = dbFolderInfo.sortType; sortOrder = dbFolderInfo.sortOrder; viewFlags = dbFolderInfo.viewFlags; if (folderFlags & MSG_FOLDER_FLAG_VIRTUAL) { viewType = nsMsgViewType.eShowQuickSearchResults; var searchTermString = dbFolderInfo.getCharProperty("searchStr"); var searchOnline = dbFolderInfo.getBooleanProperty("searchOnline", false); // trick the view code into updating the real folder... gCurrentVirtualFolderUri = uriToLoad; viewDebug("uriToLoad = " + uriToLoad + "\n"); var srchFolderUri = dbFolderInfo.getCharProperty("searchFolderUri"); var srchFolderUriArray = srchFolderUri.split('|'); // cross folder search var filterService = Components.classes["@mozilla.org/messenger/services/filters;1"].getService(Components.interfaces.nsIMsgFilterService); var filterList = filterService.getTempFilterList(msgFolder); var tempFilter = filterList.createFilter("temp"); filterList.parseCondition(tempFilter, searchTermString); if (srchFolderUriArray.length > 1) { viewType = nsMsgViewType.eShowVirtualFolderResults; gXFVirtualFolderTerms = CreateGroupedSearchTerms(tempFilter.searchTerms); setupXFVirtualFolderSearch(srchFolderUriArray, gXFVirtualFolderTerms, searchOnline); // need to set things up so that reroot folder issues the search } else { gSearchSession = null; uriToLoad = srchFolderUri; // we need to load the db for the actual folder so that many hdrs to download // will return false... realFolder = GetMsgFolderFromUri(uriToLoad); msgDatabase = realFolder.getMsgDatabase(msgWindow); gVirtualFolderTerms = CreateGroupedSearchTerms(tempFilter.searchTerms); } } else { gSearchSession = null; viewFlags = dbFolderInfo.viewFlags; viewType = dbFolderInfo.viewType; } msgDatabase = null; dbFolderInfo = null; } } catch (ex) { dump("failed to get view & sort values. ex = " + ex +"\n"); } } if (gDBView && gDBView.viewType == nsMsgViewType.eShowQuickSearchResults) { if (gPreQuickSearchView) //close cached view before quick search { gPreQuickSearchView.close(); gPreQuickSearchView = null; } ClearQSIfNecessary(); } ClearMessagePane(); if (gXFVirtualFolderTerms) viewType = nsMsgViewType.eShowVirtualFolderResults; else if (gSearchEmailAddress || gVirtualFolderTerms) viewType = nsMsgViewType.eShowQuickSearchResults; else if (viewType == nsMsgViewType.eShowQuickSearchResults) viewType = nsMsgViewType.eShowAllThreads; //override viewType - we don't want to start w/ quick search ChangeFolder(realFolder, msgFolder, viewType, viewFlags, sortType, sortOrder); if (gVirtualFolderTerms) gDBView.viewFolder = msgFolder; } document.getElementById('tabmail').setTabTitle(null); } else { msgWindow.openFolder = null; ClearThreadPane(); } if (gAccountCentralLoaded) UpdateMailToolbar("gAccountCentralLoaded"); if (gDisplayStartupPage) { loadStartPage(); gDisplayStartupPage = false; UpdateMailToolbar("gDisplayStartupPage"); } } function ClearThreadPane() { if (gDBView) { gDBView.close(); gDBView = null; } } function IsSpecialFolder(msgFolder, flags, checkAncestors) { if (!msgFolder) return false; else if ((msgFolder.flags & flags) == 0) { var parentMsgFolder = msgFolder.parentMsgFolder; return (parentMsgFolder && checkAncestors) ? IsSpecialFolder(parentMsgFolder, flags, true) : false; } else { // the user can set their INBOX to be their SENT folder. // in that case, we want this folder to act like an INBOX, // and not a SENT folder return !((flags & MSG_FOLDER_FLAG_SENTMAIL) && (msgFolder.flags & MSG_FOLDER_FLAG_INBOX)); } } function Undo() { messenger.undo(msgWindow); } function Redo() { messenger.redo(msgWindow); } function getSearchTermString(searchTerms) { var searchIndex; var condition = ""; var count = searchTerms.Count(); for (searchIndex = 0; searchIndex < count; ) { var term = searchTerms.QueryElementAt(searchIndex++, Components.interfaces.nsIMsgSearchTerm); if (condition.length > 1) condition += ' '; if (term.matchAll) { condition = "ALL"; break; } condition += (term.booleanAnd) ? "AND (" : "OR ("; condition += term.termAsString + ')'; } return condition; } function CreateVirtualFolder(newName, parentFolder, searchFolderURIs, searchTerms, searchOnline) { // ### need to make sure view/folder doesn't exist. if (searchFolderURIs && (searchFolderURIs != "") && newName && (newName != "")) { try { var newFolder = parentFolder.addSubfolder(newName); newFolder.prettyName = newName; newFolder.setFlag(MSG_FOLDER_FLAG_VIRTUAL); var vfdb = newFolder.getMsgDatabase(msgWindow); var searchTermString = getSearchTermString(searchTerms); var dbFolderInfo = vfdb.dBFolderInfo; // set the view string as a property of the db folder info // set the original folder name as well. dbFolderInfo.setCharProperty("searchStr", searchTermString); dbFolderInfo.setCharProperty("searchFolderUri", searchFolderURIs); dbFolderInfo.setBooleanProperty("searchOnline", searchOnline); vfdb.summaryValid = true; vfdb.Close(true); parentFolder.NotifyItemAdded(newFolder); var accountManager = Components.classes["@mozilla.org/messenger/account-manager;1"].getService(Components.interfaces.nsIMsgAccountManager); accountManager.saveVirtualFolders(); } catch(e) { throw(e); // so that the dialog does not automatically close dump ("Exception : creating virtual folder \n"); } } else { dump("no name or nothing selected\n"); } } var searchSessionContractID = "@mozilla.org/messenger/searchSession;1"; var gSearchView; var gSearchSession; var nsIMsgFolder = Components.interfaces.nsIMsgFolder; var nsIMsgWindow = Components.interfaces.nsIMsgWindow; var nsIMsgRDFDataSource = Components.interfaces.nsIMsgRDFDataSource; var nsMsgSearchScope = Components.interfaces.nsMsgSearchScope; var gFolderDatasource; var gFolderPicker; var gStatusBar = null; var gMessengerBundle = null; // Datasource search listener -- made global as it has to be registered // and unregistered in different functions. var gDataSourceSearchListener; var gViewSearchListener; var gMailSession; function GetScopeForFolder(folder) { return folder.server.searchScope; } function setupXFVirtualFolderSearch(folderUrisToSearch, searchTerms, searchOnline) { var count = new Object; var i; gSearchSession = Components.classes[searchSessionContractID].createInstance(Components.interfaces.nsIMsgSearchSession); gMailSession = Components.classes[mailSessionContractID].getService(Components.interfaces.nsIMsgMailSession); for (i in folderUrisToSearch) { var realFolder = GetMsgFolderFromUri(folderUrisToSearch[i]); if (!realFolder.isServer) gSearchSession.addScopeTerm(!searchOnline ? nsMsgSearchScope.offlineMail : GetScopeForFolder(realFolder), realFolder); } var termsArray = searchTerms.QueryInterface(Components.interfaces.nsISupportsArray); for (i = 0; i < termsArray.Count(); ++i) gSearchSession.appendTerm(termsArray.GetElementAt(i).QueryInterface(Components.interfaces.nsIMsgSearchTerm)); } function CreateGroupedSearchTerms(searchTermsArray) { var searchSession = gSearchSession || Components.classes[searchSessionContractID].createInstance(Components.interfaces.nsIMsgSearchSession); // create a temporary isupports array to store our search terms // since we will be modifying the terms so they work with quick search var searchTermsArrayForQS = Components.classes["@mozilla.org/supports-array;1"].createInstance(Components.interfaces.nsISupportsArray); var numEntries = searchTermsArray.Count(); for (var i = 0; i < numEntries; i++) { var searchTerm = searchTermsArray.GetElementAt(i).QueryInterface(Components.interfaces.nsIMsgSearchTerm); // clone the term, since we might be modifying it var searchTermForQS = searchSession.createTerm(); searchTermForQS.value = searchTerm.value; searchTermForQS.attrib = searchTerm.attrib; searchTermForQS.arbitraryHeader = searchTerm.arbitraryHeader searchTermForQS.op = searchTerm.op; // mark the first node as a group if (i == 0) searchTermForQS.beginsGrouping = true; else if (i == numEntries - 1) searchTermForQS.endsGrouping = true; // turn the first term to true to work with quick search... searchTermForQS.booleanAnd = i ? searchTerm.booleanAnd : true; searchTermsArrayForQS.AppendElement(searchTermForQS); } return searchTermsArrayForQS; } function OnLeavingFolder(aFolder) { try { // Mark all messages of aFolder as read: // We can't use the command controller, because it is already tuned in to the // new folder, so we just mimic its behaviour wrt goDoCommand('cmd_markAllRead'). if (gDBView && gPrefBranch.getBoolPref("mailnews.mark_message_read." + aFolder.server.type)) { gDBView.doCommand(nsMsgViewCommandType.markAllRead); } } catch(e){/* ignore */} } var gViewDebug = false; function viewDebug(str) { if (gViewDebug) dump(str); }