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:
bienvenu%nventure.com
2007-06-27 04:36:53 +00:00
parent 32bba49dff
commit 403ebfa959
6 changed files with 451 additions and 1 deletions

View File

@@ -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()

View File

@@ -79,6 +79,7 @@ var gFakeAccountPageLoaded = false;
function OnMailWindowUnload()
{
CloseTabs();
MailOfflineMgr.uninit();
ClearPendingReadTimer();

View File

@@ -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)

View File

@@ -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;"

View File

@@ -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"/>

View File

@@ -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)