diff --git a/mailnews/base/src/nsMessengerUnixIntegration.cpp b/mailnews/base/src/nsMessengerUnixIntegration.cpp index e6f6aa6d55..57b4788e63 100644 --- a/mailnews/base/src/nsMessengerUnixIntegration.cpp +++ b/mailnews/base/src/nsMessengerUnixIntegration.cpp @@ -150,7 +150,6 @@ nsresult nsMessengerUnixIntegration::GetStringBundle(nsIStringBundle **aBundle) return NS_OK; } -#ifdef MOZ_THUNDERBIRD bool nsMessengerUnixIntegration::BuildNotificationTitle(nsIMsgFolder *aFolder, nsIStringBundle *aBundle, nsString &aTitle) { @@ -334,7 +333,6 @@ nsMessengerUnixIntegration::BuildNotificationBody(nsIMsgDBHdr *aHdr, aBody.Assign(alertBody); return true; } -#endif nsresult nsMessengerUnixIntegration::ShowAlertMessage(const nsAString& aAlertTitle, const nsAString& aAlertText, const nsACString& aFolderURI) { @@ -351,7 +349,6 @@ nsresult nsMessengerUnixIntegration::ShowAlertMessage(const nsAString& aAlertTit if (showAlert) { mAlertInProgress = true; -#ifdef MOZ_THUNDERBIRD nsCOMPtr alertsService(do_GetService(NS_SYSTEMALERTSERVICE_CONTRACTID, &rv)); if (NS_SUCCEEDED(rv)) { rv = alertsService->ShowAlertNotification(NS_LITERAL_STRING(NEW_MAIL_ALERT_ICON), @@ -367,16 +364,6 @@ nsresult nsMessengerUnixIntegration::ShowAlertMessage(const nsAString& aAlertTit AlertFinished(); rv = ShowNewAlertNotification(false); -#else - nsCOMPtr alertsService (do_GetService(NS_ALERTSERVICE_CONTRACTID, &rv)); - if (NS_SUCCEEDED(rv)) - { - rv = alertsService->ShowAlertNotification(NS_LITERAL_STRING(NEW_MAIL_ALERT_ICON), aAlertTitle, - aAlertText, true, - NS_ConvertASCIItoUTF16(aFolderURI), this, - EmptyString()); - } -#endif } if (NS_FAILED(rv)) // go straight to showing the system tray icon. @@ -385,7 +372,6 @@ nsresult nsMessengerUnixIntegration::ShowAlertMessage(const nsAString& aAlertTit return rv; } -#ifdef MOZ_THUNDERBIRD // Opening Thunderbird's new mail alert notification window for not supporting libnotify // aUserInitiated --> true if we are opening the alert notification in response to a user action // like clicking on the biff icon @@ -445,7 +431,6 @@ nsresult nsMessengerUnixIntegration::ShowNewAlertNotification(bool aUserInitiate return rv; } -#endif nsresult nsMessengerUnixIntegration::AlertFinished() { @@ -496,7 +481,6 @@ void nsMessengerUnixIntegration::FillToolTipInfo() if (folder && folderWithNewMail) { -#ifdef MOZ_THUNDERBIRD nsCOMPtr bundle; GetStringBundle(getter_AddRefs(bundle)); @@ -578,33 +562,6 @@ void nsMessengerUnixIntegration::FillToolTipInfo() // Write the newest message timestamp to the appropriate // mapping in our hashtable of MRUTime's. PutMRUTimestampForFolder(folder, dateInSeconds); -#else - nsString accountName; - folder->GetPrettiestName(accountName); - - nsCOMPtr bundle; - GetStringBundle(getter_AddRefs(bundle)); - if (bundle) - { - int32_t numNewMessages = 0; - folder->GetNumNewMessages(true, &numNewMessages); - nsAutoString numNewMsgsText; - numNewMsgsText.AppendInt(numNewMessages); - - const PRUnichar *formatStrings[] = - { - numNewMsgsText.get(), - }; - - nsString finalText; - if (numNewMessages == 1) - bundle->FormatStringFromName(NS_LITERAL_STRING("biffNotification_message").get(), formatStrings, 1, getter_Copies(finalText)); - else - bundle->FormatStringFromName(NS_LITERAL_STRING("biffNotification_messages").get(), formatStrings, 1, getter_Copies(finalText)); - - ShowAlertMessage(accountName, finalText, EmptyCString()); - } // if we got a bundle -#endif } // if we got a folder } @@ -775,11 +732,9 @@ nsMessengerUnixIntegration::OnStartRunningUrl(nsIURI *aUrl) NS_IMETHODIMP nsMessengerUnixIntegration::OnStopRunningUrl(nsIURI *aUrl, nsresult aExitCode) { -#ifdef MOZ_THUNDERBIRD if (NS_SUCCEEDED(aExitCode)) // preview fetch is done. FillToolTipInfo(); -#endif return NS_OK; } @@ -799,7 +754,6 @@ nsMessengerUnixIntegration::GetMRUTimestampForFolder(nsIMsgFolder *aFolder, return NS_OK; } -#ifdef MOZ_THUNDERBIRD nsresult nsMessengerUnixIntegration::PutMRUTimestampForFolder(nsIMsgFolder *aFolder, uint32_t aLastMRUTime) @@ -815,5 +769,3 @@ nsMessengerUnixIntegration::PutMRUTimestampForFolder(nsIMsgFolder *aFolder, return NS_OK; } -#endif - diff --git a/mailnews/base/src/nsMessengerUnixIntegration.h b/mailnews/base/src/nsMessengerUnixIntegration.h index 1ebe4fcd83..54e9ff977a 100644 --- a/mailnews/base/src/nsMessengerUnixIntegration.h +++ b/mailnews/base/src/nsMessengerUnixIntegration.h @@ -45,12 +45,10 @@ private: void FillToolTipInfo(); nsresult GetMRUTimestampForFolder(nsIMsgFolder *aFolder, uint32_t *aLastMRUTime); -#ifdef MOZ_THUNDERBIRD bool BuildNotificationBody(nsIMsgDBHdr *aHdr, nsIStringBundle *Bundle, nsString &aBody); bool BuildNotificationTitle(nsIMsgFolder *aFolder, nsIStringBundle *aBundle, nsString &aTitle); nsresult ShowNewAlertNotification(bool aUserInitiated); nsresult PutMRUTimestampForFolder(nsIMsgFolder *aFolder, uint32_t aLastMRUTime); -#endif nsCOMPtr mFoldersWithNewMail; // keep track of all the root folders with pending new mail nsCOMPtr mBiffStateAtom; diff --git a/mailnews/base/src/nsMessengerWinIntegration.cpp b/mailnews/base/src/nsMessengerWinIntegration.cpp index d7428071f0..37e4c25019 100644 --- a/mailnews/base/src/nsMessengerWinIntegration.cpp +++ b/mailnews/base/src/nsMessengerWinIntegration.cpp @@ -68,6 +68,8 @@ #define SHOW_ALERT_PREF "mail.biff.show_alert" #define SHOW_TRAY_ICON_PREF "mail.biff.show_tray_icon" #define SHOW_BALLOON_PREF "mail.biff.show_balloon" +#define SHOW_NEW_ALERT_PREF "mail.biff.show_new_alert" +#define ALERT_ORIGIN_PREF "ui.alertNotificationOrigin" // since we are including windows.h in this file, undefine get user name.... #ifdef GetUserName @@ -86,6 +88,13 @@ #define NIN_BALLOONUSERCLICK (WM_USER + 5) #endif +#ifndef MOZ_THUNDERBIRD +// from LookAndFeel.h +#define NS_ALERT_HORIZONTAL 1 +#define NS_ALERT_LEFT 2 +#define NS_ALERT_TOP 4 +#endif + using namespace mozilla; // begin shameless copying from nsNativeAppSupportWin @@ -165,7 +174,6 @@ static void CALLBACK delayedSingleClick(HWND msgWindow, UINT msg, INT_PTR idEven { ::KillTimer(msgWindow, idEvent); -#ifdef MOZ_THUNDERBIRD // single clicks on the biff icon should re-open the alert notification nsresult rv = NS_OK; nsCOMPtr integrationService = @@ -177,7 +185,6 @@ static void CALLBACK delayedSingleClick(HWND msgWindow, UINT msg, INT_PTR idEven (static_cast(integrationService.get())); winIntegrationService->ShowNewAlertNotification(true, EmptyString(), EmptyString()); } -#endif } // Window proc. @@ -448,7 +455,7 @@ nsresult nsMessengerWinIntegration::ShowAlertMessage(const nsString& aAlertTitle return rv; } -#else +#endif // Opening Thunderbird's new mail alert notification window // aUserInitiated --> true if we are opening the alert notification in response to a user action // like clicking on the biff icon @@ -519,9 +526,47 @@ nsresult nsMessengerWinIntegration::ShowNewAlertNotification(bool aUserInitiated nsCOMPtr scriptableOrigin (do_CreateInstance(NS_SUPPORTS_PRUINT8_CONTRACTID)); NS_ENSURE_TRUE(scriptableOrigin, NS_ERROR_FAILURE); scriptableOrigin->SetData(0); - int32_t origin = LookAndFeel::GetInt(LookAndFeel::eIntID_AlertNotificationOrigin); - if (origin && origin >= 0 && origin <= 7) - scriptableOrigin->SetData(origin); + int32_t origin = 0; +#ifdef MOZ_THUNDERBIRD + origin = LookAndFeel::GetInt(LookAndFeel::eIntID_AlertNotificationOrigin); +#else + // Get task bar window handle + HWND shellWindow = FindWindowW(L"Shell_TrayWnd", NULL); + + rv = prefBranch->GetIntPref(ALERT_ORIGIN_PREF, &origin); + if (NS_FAILED(rv) && (shellWindow != NULL)) + { + // Determine position + APPBARDATA appBarData; + appBarData.hWnd = shellWindow; + appBarData.cbSize = sizeof(appBarData); + if (SHAppBarMessage(ABM_GETTASKBARPOS, &appBarData)) + { + // Set alert origin as a bit field - see LookAndFeel.h + // 0 represents bottom right, sliding vertically. + switch(appBarData.uEdge) + { + case ABE_LEFT: + origin = NS_ALERT_HORIZONTAL | NS_ALERT_LEFT; + break; + case ABE_RIGHT: + origin = NS_ALERT_HORIZONTAL; + break; + case ABE_TOP: + origin = NS_ALERT_TOP; + // fall through for the right-to-left handling. + case ABE_BOTTOM: + // If the task bar is right-to-left, + // move the origin to the left + if (::GetWindowLong(shellWindow, GWL_EXSTYLE) & + WS_EX_LAYOUTRTL) + origin |= NS_ALERT_LEFT; + break; + } + } + } +#endif + scriptableOrigin->SetData(origin); rv = argsArray->AppendElement(scriptableOrigin, false); NS_ENSURE_SUCCESS(rv, rv); @@ -542,7 +587,6 @@ nsresult nsMessengerWinIntegration::ShowNewAlertNotification(bool aUserInitiated return rv; } -#endif nsresult nsMessengerWinIntegration::AlertFinished() { @@ -567,7 +611,6 @@ nsresult nsMessengerWinIntegration::AlertFinished() nsresult nsMessengerWinIntegration::AlertClicked() { -#ifdef MOZ_THUNDERBIRD nsresult rv; nsCOMPtr mailSession = do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv,rv); @@ -583,7 +626,6 @@ nsresult nsMessengerWinIntegration::AlertClicked() return NS_OK; } } -#endif // make sure we don't insert the icon in the system tray since the user clicked on the alert. mSuppressBiffIcon = true; nsCString folderURI; @@ -700,10 +742,17 @@ void nsMessengerWinIntegration::FillToolTipInfo() if (!mBiffIconVisible) { #ifndef MOZ_THUNDERBIRD + nsresult rv; + bool showNewAlert = false; + nsCOMPtr prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS_VOID(rv); + + prefBranch->GetBoolPref(SHOW_NEW_ALERT_PREF, &showNewAlert); + if (!showNewAlert) ShowAlertMessage(accountName, animatedAlertText, EmptyCString()); -#else - ShowNewAlertNotification(false, accountName, animatedAlertText); + else #endif + ShowNewAlertNotification(false, accountName, animatedAlertText); } else GenericShellNotify( NIM_MODIFY); diff --git a/mailnews/base/src/nsMessengerWinIntegration.h b/mailnews/base/src/nsMessengerWinIntegration.h index 5bc7022596..50680c5cc2 100644 --- a/mailnews/base/src/nsMessengerWinIntegration.h +++ b/mailnews/base/src/nsMessengerWinIntegration.h @@ -56,9 +56,8 @@ public: NS_DECL_NSIFOLDERLISTENER NS_DECL_NSIOBSERVER -#ifdef MOZ_THUNDERBIRD nsresult ShowNewAlertNotification(bool aUserInitiated, const nsString& aAlertTitle, const nsString& aAlertText); -#else +#ifndef MOZ_THUNDERBIRD nsresult ShowAlertMessage(const nsString& aAlertTitle, const nsString& aAlertText, const nsACString& aFolderURI); #endif diff --git a/suite/browser/browser-prefs.js b/suite/browser/browser-prefs.js index daf549d55e..6c4906986b 100644 --- a/suite/browser/browser-prefs.js +++ b/suite/browser/browser-prefs.js @@ -329,6 +329,7 @@ pref("mail.biff.alert.show_preview", true); pref("mail.biff.alert.show_subject", true); pref("mail.biff.alert.show_sender", true); pref("mail.biff.alert.preview_length", 40); +pref("mail.biff.show_new_alert", true); pref("mailnews.ui.deleteMarksRead", true); diff --git a/suite/locales/en-US/chrome/mailnews/messenger.properties b/suite/locales/en-US/chrome/mailnews/messenger.properties index 56104bb7a0..7bed4802c9 100644 --- a/suite/locales/en-US/chrome/mailnews/messenger.properties +++ b/suite/locales/en-US/chrome/mailnews/messenger.properties @@ -323,6 +323,12 @@ macBiffNotification_messages_extra=%1$S new messages from %2$S and %3$S more. # Used to separate names/email addresses in a list. Note the trailing space ', ' macBiffNotification_separator=,\u0020 +# LOCALIZATION NOTE(newMailNotification_message): %1$S is the name of the account %2$S is the number of new messages +newMailNotification_message=%1$S received %2$S new message + +# LOCALIZATION NOTE(newMailNotification_messages): %1$S is the name of the account %2$S is the number of new messages +newMailNotification_messages=%1$S received %2$S new messages + # For the Quota tab in the mail folder properties dialog quotaUsedFree=%S of %S KB used quotaPercentUsed=%S%% full diff --git a/suite/mailnews/jar.mn b/suite/mailnews/jar.mn index 8b93c23e84..64ce893f01 100644 --- a/suite/mailnews/jar.mn +++ b/suite/mailnews/jar.mn @@ -58,6 +58,9 @@ messenger.jar: content/messenger/mail-offline.js content/messenger/mailContextMenus.js content/messenger/msgFolderPickerOverlay.xul + content/messenger/newmailalert.css + content/messenger/newmailalert.js + content/messenger/newmailalert.xul content/messenger/start.xhtml content/messenger/messengerdnd.js content/messenger/mailPrefsOverlay.xul (prefs/mailPrefsOverlay.xul) diff --git a/suite/mailnews/newmailalert.css b/suite/mailnews/newmailalert.css new file mode 100644 index 0000000000..cd0087a46d --- /dev/null +++ b/suite/mailnews/newmailalert.css @@ -0,0 +1,23 @@ +/* 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/. */ + +#alertContainer { + opacity: 0; +} + +#alertContainer[noanimation] { + opacity: 1; +} + +#alertContainer[animate] { + animation-timing-function: ease-out; + animation-duration: 4s; + animation-fill-mode: both; + animation-name: alert-animation; +} + +@keyframes alert-animation { + from {opacity: 0;} + to {opacity: 1;} +} diff --git a/suite/mailnews/newmailalert.js b/suite/mailnews/newmailalert.js new file mode 100644 index 0000000000..febbdeb104 --- /dev/null +++ b/suite/mailnews/newmailalert.js @@ -0,0 +1,181 @@ +/* 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/. */ + +Components.utils.import("resource://gre/modules/Services.jsm"); + +// Copied from nsILookAndFeel.h, see comments on eMetric_AlertNotificationOrigin +const NS_ALERT_HORIZONTAL = 1; +const NS_ALERT_LEFT = 2; +const NS_ALERT_TOP = 4; + +var gNumNewMsgsToShowInAlert = 4; // the more messages we show in the alert, the larger it will be +var gOpenTime = 4000; // total time the alert should stay up once we are done animating. + +var gAlertListener = null; +var gPendingPreviewFetchRequests = 0; +var gUserInitiated = false; +var gOrigin = 0; // Default value: alert from bottom right. + +function prefillAlertInfo() +{ + // unwrap all the args.... + // arguments[0] --> array of folders with new mail + // arguments[1] --> the observer to call back with notifications about the alert + // arguments[2] --> user initiated boolean. true if the user initiated opening the alert + // (which means skip the fade effect and don't auto close the alert) + // arguments[3] --> the alert origin returned by the look and feel + var foldersWithNewMail = window.arguments[0]; + gAlertListener = window.arguments[1]; + gUserInitiated = window.arguments[2]; + gOrigin = window.arguments[3]; + + // For now just grab the first folder which should be a root folder + // for the account that has new mail. If we can't find a folder, just + // return to avoid the exception and empty dialog in upper left-hand corner. + var rootFolder; + if (!foldersWithNewMail || foldersWithNewMail.Count() < 1) + return; + rootFolder = foldersWithNewMail.GetElementAt(0) + .QueryInterface(Components.interfaces.nsIWeakReference) + .QueryReferent(Components.interfaces.nsIMsgFolder); + + // Generate an account label string based on the root folder. + var label = document.getElementById('alertTitle'); + var totalNumNewMessages = rootFolder.getNumNewMessages(true); + var message = totalNumNewMessages == 1 ? "newMailNotification_message" + : "newMailNotification_messages"; + label.value = document.getElementById('bundle_messenger') + .getFormattedString(message, + [rootFolder.prettiestName, totalNumNewMessages]); + + // This is really the root folder and we have to walk through the list to + // find the real folder that has new mail in it...:( + var allFolders = Components.classes["@mozilla.org/supports-array;1"] + .createInstance(Components.interfaces.nsISupportsArray); + rootFolder.ListDescendents(allFolders); + var numFolders = allFolders.Count(); + var folderSummaryInfoEl = document.getElementById('folderSummaryInfo'); + folderSummaryInfoEl.mMaxMsgHdrsInPopup = gNumNewMsgsToShowInAlert; + for (let folderIndex = 0; folderIndex < numFolders; folderIndex++) + { + var folder = allFolders.GetElementAt(folderIndex) + .QueryInterface(Components.interfaces.nsIMsgFolder); + const nsMsgFolderFlags = Components.interfaces.nsMsgFolderFlags; + if (folder.hasNewMessages && !(folder.flags & nsMsgFolderFlags.Virtual)) + { + var asyncFetch = {}; + folderSummaryInfoEl.parseFolder(folder, new urlListener(folder), asyncFetch); + if (asyncFetch.value) + gPendingPreviewFetchRequests++; + } + } +} + +function urlListener(aFolder) +{ + this.mFolder = aFolder; +} + +urlListener.prototype = +{ + OnStartRunningUrl: function(aUrl) + { + }, + + OnStopRunningUrl: function(aUrl, aExitCode) + { + var folderSummaryInfoEl = document.getElementById('folderSummaryInfo'); + folderSummaryInfoEl.parseFolder(this.mFolder, null, {}); + gPendingPreviewFetchRequests--; + + // when we are done running all of our urls for fetching the preview text, + // start the alert. + if (!gPendingPreviewFetchRequests) + showAlert(); + } +} + +function onAlertLoad() +{ + prefillAlertInfo(); + // read out our initial settings from prefs. + try + { + gOpenTime = Services.prefs.getIntPref("alerts.totalOpenTime"); + } catch (ex) {} + + // bogus call to make sure the window is moved offscreen until we are ready for it. + resizeAlert(true); + + // if we aren't waiting to fetch preview text, then go ahead and + // start showing the alert. + if (!gPendingPreviewFetchRequests) + setTimeout(showAlert, 0); // let the JS thread unwind, to give layout + // a chance to recompute the styles and widths for our alert text. +} + +// If the user initiated the alert, show it right away, otherwise start opening the alert with +// the fade effect. +function showAlert() +{ + // resize the alert based on our current content + resizeAlert(false); + + var alertContainer = document.getElementById("alertContainer"); + // Don't fade in if the user opened the alert or the pref is true. + if (gUserInitiated || + Services.prefs.getBoolPref("alerts.disableSlidingEffect")) { + alertContainer.setAttribute("noanimation", true); + setTimeout(closeAlert, gOpenTime); + return; + } + + if (document.getElementById('folderSummaryInfo').hasMessages) + { + alertContainer.addEventListener("animationend", function hideAlert(event) { + if (event.animationName == "alert-animation") { + alertContainer.removeEventListener("animationend", hideAlert, false); + let remaining = Math.max(Math.round(gOpenTime - event.elapsedTime * 1000), 0); + setTimeout(closeAlert, remaining); + } + }, false); + alertContainer.setAttribute("animate", true); + } + else + { + closeAlert(); // no mail, so don't bother showing the alert... + } +} + +function resizeAlert(aMoveOffScreen) +{ + var alertTextBox = document.getElementById("alertTextBox"); + var alertImageBox = document.getElementById("alertImageBox"); + alertImageBox.style.minHeight = alertTextBox.scrollHeight + "px"; + + sizeToContent(); + + // leftover hack to get the window properly hidden when we first open it + if (aMoveOffScreen) + window.outerHeight = 1; + + // Determine position + var x = gOrigin & NS_ALERT_LEFT ? screen.availLeft : + screen.availLeft + screen.availWidth - window.outerWidth; + var y = gOrigin & NS_ALERT_TOP ? screen.availTop : + screen.availTop + screen.availHeight - window.outerHeight; + + // Offset the alert by 10 pixels from the edge of the screen + y += gOrigin & NS_ALERT_TOP ? 10 : -10; + x += gOrigin & NS_ALERT_LEFT ? 10 : -10; + + window.moveTo(x, y); +} + +function closeAlert() +{ + if (gAlertListener) + gAlertListener.observe(null, "alertfinished", ""); + window.close(); +} diff --git a/suite/mailnews/newmailalert.xul b/suite/mailnews/newmailalert.xul new file mode 100644 index 0000000000..2df37494f6 --- /dev/null +++ b/suite/mailnews/newmailalert.xul @@ -0,0 +1,35 @@ + + + + + + + + + + +