start work on tabbed e-mail browsing, sr=mscott 218999
git-svn-id: svn://10.0.0.236/trunk@228816 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
@@ -348,6 +348,7 @@ function RerootFolder(uri, newFolder, viewType, viewFlags, sortType, sortOrder)
|
||||
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);
|
||||
@@ -996,6 +997,7 @@ function FolderPaneSelectionChange()
|
||||
if (gVirtualFolderTerms)
|
||||
gDBView.viewFolder = msgFolder;
|
||||
}
|
||||
UpdateCurTabTitle(msgFolder.prettyName);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1013,7 +1015,7 @@ function FolderPaneSelectionChange()
|
||||
loadStartPage();
|
||||
gDisplayStartupPage = false;
|
||||
UpdateMailToolbar("gDisplayStartupPage");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function ClearThreadPane()
|
||||
|
||||
@@ -79,6 +79,7 @@ var gFakeAccountPageLoaded = false;
|
||||
|
||||
function OnMailWindowUnload()
|
||||
{
|
||||
CloseTabs();
|
||||
MailOfflineMgr.uninit();
|
||||
ClearPendingReadTimer();
|
||||
|
||||
|
||||
@@ -1344,6 +1344,397 @@ function MsgOpenNewWindowForFolder(uri, key)
|
||||
window.openDialog("chrome://messenger/content/", "_blank", "chrome,all,dialog=no", uriToOpen, keyToSelect);
|
||||
}
|
||||
|
||||
var gTabInfoArray = new Array;
|
||||
var gCurTabInfo; // used when switching tabs to know what prev tab was.
|
||||
function GetCurTabIndex()
|
||||
{
|
||||
var tabContainer = document.getElementById("tabcontainer");
|
||||
return tabContainer.selectedIndex;
|
||||
}
|
||||
|
||||
function UpdateCurTabTitle(title)
|
||||
{
|
||||
try
|
||||
{
|
||||
var tabContainer = document.getElementById("tabcontainer");
|
||||
tabContainer.selectedItem.setAttribute("label", title);
|
||||
}
|
||||
catch(ex) {
|
||||
dump(ex);
|
||||
}
|
||||
}
|
||||
|
||||
function CloseTabs()
|
||||
{
|
||||
for (var i = 0; i < gTabInfoArray.length; i++)
|
||||
{
|
||||
if (gTabInfoArray[i] != gCurTabInfo)
|
||||
gTabInfoArray[i].close();
|
||||
}
|
||||
}
|
||||
|
||||
function UpdateMsgTabTitle()
|
||||
{
|
||||
// if current tab is a msg tab, update title.
|
||||
if (gCurTabInfo && gCurTabInfo.type == "message")
|
||||
UpdateCurTabTitle(msgHdrForCurrentMessage().mime2DecodedSubject);
|
||||
}
|
||||
|
||||
function SaveCurrentTabInfo()
|
||||
{
|
||||
if (!gCurTabInfo)
|
||||
gCurTabInfo = gTabInfoArray[0];
|
||||
gCurTabInfo.saveCurrentInfo();
|
||||
}
|
||||
|
||||
function SaveTabInfo(tab)
|
||||
{
|
||||
if (tab)
|
||||
{
|
||||
tab.messenger = messenger;
|
||||
tab.dbView = gDBView;
|
||||
tab.searchSession = gSearchSession;
|
||||
var indices = GetSelectedIndices(gDBView);
|
||||
if (indices)
|
||||
{
|
||||
tab.selectedKeys = new Array(indices.length);
|
||||
tab.selectedFolders = new Array(indices.length);
|
||||
}
|
||||
if (gDBView.currentlyDisplayedMessage != -1)
|
||||
{
|
||||
try // there may not be a selected message.
|
||||
{
|
||||
var curMsgHdr = gDBView.hdrForFirstSelectedMessage;
|
||||
tab.selectedMsgId = curMsgHdr.messageId;
|
||||
tab.msgSelectedFolder = curMsgHdr.folder;
|
||||
}
|
||||
catch (ex) {tab.msgSelectedFolder = gMsgFolderSelected};
|
||||
}
|
||||
else
|
||||
{
|
||||
tab.selectedMsgId = null;
|
||||
tab.msgSelectedFolder = null;
|
||||
}
|
||||
if (indices)
|
||||
{
|
||||
for (var i = 0; i < indices.length; i++)
|
||||
{
|
||||
tab.selectedKeys[i] = gDBView.getKeyAt(i);
|
||||
tab.selectedFolders[i] = gDBView.getFolderForViewIndex(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function DisplayCurTab(tab)
|
||||
{
|
||||
|
||||
messenger = tab.messenger;
|
||||
gDBView = tab.dbView;
|
||||
gSearchSession = tab.searchSession;
|
||||
gCurTabInfo = tab;
|
||||
// show/hide the message and thread pane based on whether this tab
|
||||
// is displaying a single message or not.
|
||||
DisplayFolderAndThreadPane(gCurTabInfo.type != "message");
|
||||
|
||||
if (gDBView)
|
||||
{
|
||||
var folderTree = GetFolderTree();
|
||||
var row = EnsureFolderIndex(folderTree.builderView, gDBView.msgFolder);
|
||||
|
||||
var folderTreeBoxObj = folderTree.treeBoxObject;
|
||||
var folderTreeSelection = folderTreeBoxObj.view.selection;
|
||||
|
||||
dump("finding row for folder " + gDBView.msgFolder.URI + " row = " + row + "\n");
|
||||
// make sure that row.value is valid so that it doesn't mess up
|
||||
// the call to ensureRowIsVisible().
|
||||
if((row >= 0) && !folderTreeSelection.isSelected(row))
|
||||
{
|
||||
gMsgFolderSelected = gDBView.msgFolder;
|
||||
folderTreeSelection.selectEventsSuppressed = true;
|
||||
folderTreeSelection.select(row);
|
||||
folderTreeBoxObj.ensureRowIsVisible(row);
|
||||
folderTreeSelection.selectEventsSuppressed = false;
|
||||
}
|
||||
// this sets the thread pane tree's view to the gDBView view.
|
||||
UpdateSortIndicators(gDBView.sortType, gDBView.sortOrder);
|
||||
RerootThreadPane();
|
||||
// we need to restore the selection to what it was when we switched away from this tab.
|
||||
// we need to remember the selected keys, instead of the selected indices, since the view
|
||||
// might have changed. But maybe the selectedIndices adjust as items are added/removed from
|
||||
// the (hidden) view.
|
||||
ClearThreadPaneSelection();
|
||||
try
|
||||
{
|
||||
if (gCurTabInfo.selectedMsgId && gCurTabInfo.msgSelectedFolder)
|
||||
{
|
||||
var msgDB = gCurTabInfo.msgSelectedFolder.getMsgDatabase(msgWindow);
|
||||
var msgHdr = msgDB.getMsgHdrForMessageID(gCurTabInfo.selectedMsgId);
|
||||
setTimeout(gDBView.selectFolderMsgByKey, 0, gCurTabInfo.msgSelectedFolder, msgHdr.messageKey);
|
||||
}
|
||||
}
|
||||
catch (ex) {dump(ex);}
|
||||
}
|
||||
else
|
||||
{
|
||||
var tree = GetThreadTree();
|
||||
tree.boxObject.QueryInterface(Components.interfaces.nsITreeBoxObject).view = null;
|
||||
ClearMessagePane();
|
||||
}
|
||||
}
|
||||
|
||||
function UpdateSelectedTab()
|
||||
{
|
||||
// switch tabs
|
||||
var tabContainer = document.getElementById("tabcontainer");
|
||||
SaveCurrentTabInfo();
|
||||
// if this isn't set, then this is the first time we've switched tabs, so the
|
||||
// old tab must be the 0th tab.
|
||||
var curTab = gTabInfoArray[tabContainer.selectedIndex];
|
||||
curTab.onSelect();
|
||||
}
|
||||
|
||||
function CreateToolbarTooltip(document, event)
|
||||
{
|
||||
event.stopPropagation();
|
||||
var tn = document.tooltipNode;
|
||||
if (tn.localName != "tab")
|
||||
return false; // Not a tab, so cancel the tooltip
|
||||
if ("mOverCloseButton" in tn && tn.mOverCloseButton) {
|
||||
event.target.setAttribute("label", tn.getAttribute("closetabtext"));
|
||||
return true;
|
||||
}
|
||||
if (tn.hasAttribute("label")) {
|
||||
event.target.setAttribute("label", tn.getAttribute("label"));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function DisplayFolderAndThreadPane(show)
|
||||
{
|
||||
var collapse = !show;
|
||||
if (gCurrentPaneConfig == kWidePaneConfig)
|
||||
{
|
||||
document.getElementById("mailContent").collapsed = collapse;
|
||||
// if opening a standalone message, need to give the messagepanebox flex.
|
||||
if (collapse)
|
||||
document.getElementById("messagepanebox").flex = 1;
|
||||
}
|
||||
document.getElementById("threadpane-splitter").collapsed = collapse;
|
||||
document.getElementById("folderpane_splitter").collapsed = collapse;
|
||||
document.getElementById("folderPaneBox").collapsed = collapse;
|
||||
document.getElementById("messengerBox").collapsed = collapse;
|
||||
try {
|
||||
document.getElementById("search-container").collapsed = collapse;
|
||||
} catch (ex) {}
|
||||
try {
|
||||
document.getElementById("folder-location-container").collapsed = collapse;
|
||||
} catch (ex) {}
|
||||
try {
|
||||
document.getElementById("mailviews-container").collapsed = collapse;
|
||||
} catch (ex) {}
|
||||
}
|
||||
|
||||
function MsgCloseTab(aTab)
|
||||
{
|
||||
|
||||
var tabContainer = document.getElementById("tabcontainer");
|
||||
var strip = document.getElementById("tabstrip");
|
||||
|
||||
var numTabs = tabContainer.childNodes.length;
|
||||
if (numTabs < 3)
|
||||
{
|
||||
// hide the tab bar
|
||||
strip.collapsed = true;
|
||||
if (numTabs == 1) // can this happen?
|
||||
return;
|
||||
}
|
||||
var i;
|
||||
// Find and locate the tab in our list.
|
||||
for (i = 0; i < numTabs; i++)
|
||||
if (tabContainer.childNodes[i] == aTab)
|
||||
break;
|
||||
var tabInfo = gTabInfoArray[i];
|
||||
tabInfo.close();
|
||||
gTabInfoArray.splice(i, 1);
|
||||
tabContainer.removeChild(aTab);
|
||||
if (tabContainer.selectedIndex == -1)
|
||||
tabContainer.selectedIndex = (i == --numTabs) ? i - 1 : i;
|
||||
if (gCurTabInfo == tabInfo)
|
||||
UpdateSelectedTab();
|
||||
}
|
||||
|
||||
function OpenFolderTab(tab)
|
||||
{
|
||||
ClearThreadPaneSelection();
|
||||
CreateMessenger();
|
||||
messenger.SetWindow(window, msgWindow);
|
||||
tab.msgSelectedFolder = gMsgFolderSelected;
|
||||
// clear selection, because context clicking on a folder and opening in a new
|
||||
// tab needs to have SelectFolder think the selection has changed.
|
||||
// We also need to clear these globals to subvert the code that prevents
|
||||
// folder loads when things haven't changed.
|
||||
GetFolderTree().view.selection.clearSelection();
|
||||
GetFolderTree().view.selection.currentIndex = -1;
|
||||
gMsgFolderSelected = null;
|
||||
msgWindow.openFolder = null;
|
||||
// clear thread pane selection - otherwise, the tree tries to impose
|
||||
// the current selection on the new view.
|
||||
gDBView = null; // clear gDBView so we won't try to close it.
|
||||
SelectFolder(tab.uriToOpen);
|
||||
tab.dbView = gDBView;
|
||||
}
|
||||
|
||||
function msgFolderTab()
|
||||
{
|
||||
}
|
||||
|
||||
msgFolderTab.prototype =
|
||||
{
|
||||
open : function ()
|
||||
{
|
||||
OpenFolderTab(this);
|
||||
},
|
||||
|
||||
close : function ()
|
||||
{
|
||||
if (this.dbView)
|
||||
this.dbView.close();
|
||||
if (this.messenger)
|
||||
this.messenger.SetWindow(null, null);
|
||||
},
|
||||
|
||||
saveCurrentInfo : function()
|
||||
{
|
||||
SaveTabInfo(this);
|
||||
},
|
||||
onSelect : function()
|
||||
{
|
||||
ClearMessagePane();
|
||||
DisplayCurTab(this);
|
||||
},
|
||||
|
||||
type : "folder",
|
||||
};
|
||||
|
||||
function msgMessageTab()
|
||||
{
|
||||
}
|
||||
|
||||
msgMessageTab.prototype =
|
||||
{
|
||||
open : function ()
|
||||
{
|
||||
OpenFolderTab(this);
|
||||
gCurrentlyDisplayedMessage = -1;
|
||||
ClearThreadPaneSelection();
|
||||
setTimeout(gDBView.selectFolderMsgByKey, 0, this.hdr.folder, this.hdr.messageKey);
|
||||
UpdateCurTabTitle(this.hdr.mime2DecodedSubject);
|
||||
// let's try hiding the thread pane and folder pane
|
||||
DisplayFolderAndThreadPane(false);
|
||||
},
|
||||
|
||||
close : function ()
|
||||
{
|
||||
if (this.dbView)
|
||||
this.dbView.close();
|
||||
if (this.messenger)
|
||||
this.messenger.SetWindow(null, null);
|
||||
},
|
||||
saveCurrentInfo : function()
|
||||
{
|
||||
SaveTabInfo(this);
|
||||
},
|
||||
onSelect : function()
|
||||
{
|
||||
ClearMessagePane();
|
||||
DisplayCurTab(this);
|
||||
},
|
||||
type : "message",
|
||||
|
||||
};
|
||||
|
||||
function OpenNewTab(tab)
|
||||
{
|
||||
var tabContainer = document.getElementById("tabcontainer");
|
||||
var strip = document.getElementById("tabstrip");
|
||||
var t = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
|
||||
"tab");
|
||||
|
||||
t.setAttribute("crop", "end");
|
||||
t.maxWidth = 250;
|
||||
// t.minWidth = this.mTabContainer.mTabMinWidth;
|
||||
t.width = 0;
|
||||
t.setAttribute("flex", "100");
|
||||
t.setAttribute("validate", "never");
|
||||
t.className = "tabemail-tab";
|
||||
|
||||
if (!gTabInfoArray.length)
|
||||
{
|
||||
// set up the first tab, which was previously invisible.
|
||||
var firstTabInfo = new msgFolderTab();
|
||||
firstTabInfo.messenger = messenger;
|
||||
gTabInfoArray[0] = firstTabInfo;
|
||||
}
|
||||
tabContainer.appendChild(t);
|
||||
if (strip.collapsed)
|
||||
{
|
||||
strip.collapsed = false;
|
||||
strip.setAttribute("closebuttons", "alltabs");
|
||||
}
|
||||
SaveCurrentTabInfo();
|
||||
tabContainer.selectedIndex = tabContainer.childNodes.length - 1;
|
||||
gTabInfoArray[tabContainer.selectedIndex] = tab;
|
||||
gCurTabInfo = tab;
|
||||
tab.open();
|
||||
}
|
||||
|
||||
function MsgOpenNewTabForFolder(uri, key)
|
||||
{
|
||||
var uriToOpen = uri;
|
||||
var keyToSelect = key;
|
||||
|
||||
if (!uriToOpen)
|
||||
// use GetSelectedFolderURI() to find out which message to open instead of
|
||||
// GetLoadedMsgFolder().QueryIntervace(Components.interfaces.nsIRDFResource).value.
|
||||
// This is required because on a right-click, the currentIndex value will be
|
||||
// different from the actual row that is highlighted. GetSelectedFolderURI()
|
||||
// will return the message that is highlighted.
|
||||
uriToOpen = GetSelectedFolderURI();
|
||||
|
||||
// set up the first tab, which was previously invisible.
|
||||
// This assumes the first tab is always a 3-pane ui, which
|
||||
// may not be right, especially if we have the ability
|
||||
// to persist your tab setup.
|
||||
var newTab = new msgFolderTab();
|
||||
newTab.uriToOpen = uriToOpen;
|
||||
OpenNewTab(newTab);
|
||||
}
|
||||
|
||||
function MsgOpenNewTabForMessage(messageKey, folderUri)
|
||||
{
|
||||
if (!messageKey)
|
||||
messageKey = gDBView.keyForFirstSelectedMessage;
|
||||
|
||||
var hdr = gDBView.hdrForFirstSelectedMessage;
|
||||
if (!folderUri)
|
||||
// use the header's folder - this will open a msg in a virtual folder view
|
||||
// in its real folder, which is needed if the msg wouldn't be in a new
|
||||
// view with the same terms - e.g., it's read and the view is unread only.
|
||||
// If we cloned the view, we wouldn't have to do this.
|
||||
folderUri = hdr.folder.URI;
|
||||
|
||||
// fix it so we won't try to load the previously loaded message.
|
||||
hdr.folder.lastMessageLoaded = nsMsgKey_None;
|
||||
|
||||
tab = new msgMessageTab();
|
||||
tab.uriToOpen = folderUri;
|
||||
tab.hdr = hdr;
|
||||
OpenNewTab(tab);
|
||||
}
|
||||
|
||||
// passing in the view, so this will work for search and the thread pane
|
||||
function MsgOpenSelectedMessages()
|
||||
{
|
||||
@@ -2423,6 +2814,11 @@ function OnMsgParsed(aUrl)
|
||||
// run the phishing detector on the message
|
||||
if (!checkMsgHdrPropertyIsNot("notAPhishMessage", kIsAPhishMessage))
|
||||
gPhishingDetector.analyzeMsgForPhishingURLs(aUrl);
|
||||
// notify anyone (e.g., extensions) who's interested in when a message is loaded.
|
||||
var msgURI = GetLoadedMessage();
|
||||
var observerService = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
|
||||
observerService.notifyObservers(msgWindow.msgHeaderSink, "MsgMsgDisplayed", msgURI);
|
||||
UpdateMsgTabTitle();
|
||||
}
|
||||
|
||||
function OnMsgLoaded(aUrl)
|
||||
|
||||
@@ -445,6 +445,10 @@
|
||||
label="&contextOpenNewWindow.label;"
|
||||
accesskey="&contextOpenNewWindow.accesskey;"
|
||||
oncommand="MsgOpenNewWindowForMessage();"/>
|
||||
<menuitem id="threadPaneContet-openNewTab"
|
||||
label="&contextOpenNewTab.label;"
|
||||
accesskey="contextOpenNewTab.accesskey;"
|
||||
oncommand="MsgOpenNewTabForMessage();"/>
|
||||
<menuseparator id="threadPaneContext-sep-open"/>
|
||||
<menuitem id="threadPaneContext-replySender"
|
||||
label="&contextReplySender.label;"
|
||||
@@ -734,6 +738,10 @@
|
||||
label="&folderContextOpenNewWindow.label;"
|
||||
accesskey="&folderContextOpenNewWindow.accesskey;"
|
||||
oncommand="MsgOpenNewWindowForFolder(null,-1);"/>
|
||||
<menuitem id="folderPaneContext-openNewWindow"
|
||||
label="&folderContextOpenNewTab.label;"
|
||||
accesskey="&folderContextOpenNewTab.accesskey;"
|
||||
oncommand="MsgOpenNewTabForFolder(null,-1);"/>
|
||||
|
||||
<menuitem id="folderPaneContext-searchMessages"
|
||||
label="&folderContextSearchMessages.label;"
|
||||
|
||||
@@ -164,6 +164,40 @@
|
||||
|
||||
<toolbox id="mail-toolbox" class="toolbox-top">
|
||||
</toolbox>
|
||||
|
||||
<tabbox anonid="tabbox" flex="1" eventnode="document" inherits="handleCtrlPageUpDown"
|
||||
onselect="if (!event.target.localName != 'tabpanels') return; UpdateSelectedTab();">
|
||||
<hbox class="tab-drop-indicator-bar">
|
||||
<hbox class="tab-drop-indicator" mousethrough="always"/>
|
||||
</hbox>
|
||||
<hbox class="tabbrowser-strip" collapsed="true" tooltip="_child" context="_child"
|
||||
id="tabstrip"
|
||||
ondraggesture="nsDragAndDrop.startDrag(event, this.parentNode.parentNode); event.stopPropagation();"
|
||||
ondragover="nsDragAndDrop.dragOver(event, this.parentNode.parentNode); event.stopPropagation();"
|
||||
ondragdrop="nsDragAndDrop.drop(event, this.parentNode.parentNode); event.stopPropagation();"
|
||||
ondragexit="nsDragAndDrop.dragExit(event, this.parentNode.parentNode); event.stopPropagation();">
|
||||
<tooltip onpopupshowing="return CreateToolbarTooltip(document, event);"/>
|
||||
<menupopup anonid="tabContextMenu" onpopupshowing=";">
|
||||
<menuitem label="&newTabCmd.label;" accesskey="&newTabCmd.accesskey;"
|
||||
inherits="oncommand=onnewtab"/>
|
||||
<menuitem label="&closeTabCmd.label;" accesskey="&closeTabCmd.accesskey;"
|
||||
oncommand="MsgCloseTab(document.popupNode);"/>
|
||||
</menupopup>
|
||||
|
||||
<tabs class="tabbrowser-tabs" flex="1"
|
||||
id="tabcontainer"
|
||||
setfocus="false"
|
||||
onclick="UpdateSelectedTab();"
|
||||
inherits="onnewtab"
|
||||
ondblclick="this.parentNode.parentNode.parentNode.onTabBarDblClick(event);">
|
||||
<tab selected="true" validate="never"
|
||||
maxwidth="250" width="0" minwidth="100" flex="100"
|
||||
class="tabemail-tab" crop="end"/>
|
||||
</tabs>
|
||||
</hbox>
|
||||
<tabpanels flex="1" class="plain" selectedIndex="0" anonid="panelcontainer">
|
||||
|
||||
|
||||
|
||||
<!-- when we move to the alternate 3-pane, mailContentWrapper gets toggled to a vertical orientation -->
|
||||
<box id="mailContentWrapper" orient="horizontal" flex="1">
|
||||
@@ -412,6 +446,9 @@
|
||||
</hbox>
|
||||
</box> <!-- end of mailContentWrapper -->
|
||||
|
||||
</tabpanels>
|
||||
</tabbox>
|
||||
|
||||
<statusbar id="status-bar" class="chromeclass-status">
|
||||
<statusbarpanel id="unreadMessageCount"/>
|
||||
<statusbarpanel id="totalMessageCount"/>
|
||||
|
||||
@@ -1182,6 +1182,10 @@ function CycleFolderView(aCycleForward)
|
||||
|
||||
function OnLoadFolderPane()
|
||||
{
|
||||
// want to start up showing the folder pane, in case we shut down
|
||||
// with the 3-pane showing a single message.
|
||||
document.getElementById("folderpane_splitter").collapsed = false;
|
||||
document.getElementById("folderPaneBox").collapsed = false;
|
||||
UpdateFolderColumnVisibility();
|
||||
var folderUnreadCol = document.getElementById("folderUnreadCol");
|
||||
folderUnreadCol.addEventListener("DOMAttrModified", OnFolderUnreadColAttrModified, false);
|
||||
@@ -1403,6 +1407,7 @@ function ClearMessagePane()
|
||||
{
|
||||
if(gHaveLoadedMessage)
|
||||
{
|
||||
dump("clearing message pane\n");
|
||||
gHaveLoadedMessage = false;
|
||||
if (GetMessagePaneFrame().location.href != "about:blank")
|
||||
GetMessagePaneFrame().location.href = "about:blank";
|
||||
@@ -1705,6 +1710,7 @@ function SelectFolder(folderUri)
|
||||
// ancestors are expanded
|
||||
var folderIndex = EnsureFolderIndex(folderTree.builderView, msgFolder);
|
||||
ChangeSelection(folderTree, folderIndex);
|
||||
UpdateCurTabTitle(msgFolder.prettyName);
|
||||
}
|
||||
|
||||
function SelectMessage(messageUri)
|
||||
|
||||
Reference in New Issue
Block a user