--- a/mailnews/base/src/nsMessengerOSXIntegration.cpp
+++ b/mailnews/base/src/nsMessengerOSXIntegration.cpp
@@ -59,19 +59,63 @@
#include "nsIBaseWindow.h"
#include "nsIWidget.h"
#include "nsIObserverService.h"
#include "nsIPrefService.h"
#include "nsIPrefBranch.h"
#include "nsIMessengerWindowService.h"
#include "prprf.h"
#include "nsIWeakReference.h"
+#include "nsIAlertsService.h"
+#include "nsIStringBundle.h"
+#include "nsToolkitCompsCID.h"
+#include "nsINotificationsList.h"
#include <Carbon/Carbon.h>
+#define kNewMailAlertIcon "chrome://messenger/skin/icons/new-mail-alert.png"
+#define kBiffShowAlertPref "mail.biff.show_alert"
+
+static void openMailWindow(const nsACString& aFolderUri)
+{
+ nsresult rv;
+ nsCOMPtr<nsIMsgMailSession> mailSession ( do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv));
+ if (NS_FAILED(rv))
+ return;
+
+ nsCOMPtr<nsIMsgWindow> topMostMsgWindow;
+ rv = mailSession->GetTopmostMsgWindow(getter_AddRefs(topMostMsgWindow));
+ if (topMostMsgWindow)
+ {
+ if (!aFolderUri.IsEmpty())
+ {
+ nsCOMPtr<nsIMsgWindowCommands> windowCommands;
+ topMostMsgWindow->GetWindowCommands(getter_AddRefs(windowCommands));
+ if (windowCommands)
+ windowCommands->SelectFolder(aFolderUri);
+ }
+
+ nsCOMPtr<nsIDOMWindowInternal> domWindow;
+ topMostMsgWindow->GetDomWindow(getter_AddRefs(domWindow));
+ domWindow->Focus();
+ }
+ else
+ {
+ // the user doesn't have a mail window open already so open one for them...
+ nsCOMPtr<nsIMessengerWindowService> messengerWindowService =
+ do_GetService(NS_MESSENGERWINDOWSERVICE_CONTRACTID);
+ // if we want to preselect the first account with new mail,
+ // here is where we would try to generate a uri to pass in
+ // (and add code to the messenger window service to make that work)
+ if (messengerWindowService)
+ messengerWindowService->OpenMessengerWindowWithUri(
+ "mail:3pane", nsCString(aFolderUri).get(), nsMsgKey_None);
+ }
+}
+
nsMessengerOSXIntegration::nsMessengerOSXIntegration()
{
mBiffStateAtom = do_GetAtom("BiffState");
mBiffIconVisible = PR_FALSE;
mSuppressBiffIcon = PR_FALSE;
mAlertInProgress = PR_FALSE;
NS_NewISupportsArray(getter_AddRefs(mFoldersWithNewMail));
@@ -88,40 +132,42 @@ nsMessengerOSXIntegration::~nsMessengerO
NS_IMPL_ADDREF(nsMessengerOSXIntegration)
NS_IMPL_RELEASE(nsMessengerOSXIntegration)
NS_INTERFACE_MAP_BEGIN(nsMessengerOSXIntegration)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIMessengerOSIntegration)
NS_INTERFACE_MAP_ENTRY(nsIMessengerOSIntegration)
NS_INTERFACE_MAP_ENTRY(nsIFolderListener)
+ NS_INTERFACE_MAP_ENTRY(nsIObserver)
NS_INTERFACE_MAP_END
nsresult
nsMessengerOSXIntegration::Init()
{
+ // need to register a named Growl notification
nsresult rv;
+ nsCOMPtr<nsIObserverService> observerService = do_GetService("@mozilla.org/observer-service;1", &rv);
+ if (NS_SUCCEEDED(rv))
+ observerService->AddObserver(this, "before-growl-registration", PR_FALSE);
nsCOMPtr <nsIMsgAccountManager> accountManager =
do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv,rv);
// because we care if the default server changes
rv = accountManager->AddRootFolderListener(this);
NS_ENSURE_SUCCESS(rv,rv);
nsCOMPtr<nsIMsgMailSession> mailSession = do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv,rv);
// because we care if the unread total count changes
- rv = mailSession->AddFolderListener(this, nsIFolderListener::boolPropertyChanged | nsIFolderListener::intPropertyChanged);
- NS_ENSURE_SUCCESS(rv,rv);
-
- return NS_OK;
+ return mailSession->AddFolderListener(this, nsIFolderListener::boolPropertyChanged | nsIFolderListener::intPropertyChanged);
}
NS_IMETHODIMP
nsMessengerOSXIntegration::OnItemPropertyChanged(nsIMsgFolder *, nsIAtom *, char const *, char const *)
{
return NS_OK;
}
@@ -132,47 +178,246 @@ nsMessengerOSXIntegration::OnItemUnichar
}
NS_IMETHODIMP
nsMessengerOSXIntegration::OnItemRemoved(nsIMsgFolder *, nsISupports *)
{
return NS_OK;
}
-PRInt32 nsMessengerOSXIntegration::CountNewMessages()
+NS_IMETHODIMP
+nsMessengerOSXIntegration::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aData)
+{
+ if (!strcmp(aTopic, "alertfinished"))
+ return OnAlertFinished(nsnull);
+
+ if (!strcmp(aTopic, "alertclickcallback"))
+ return OnAlertClicked();
+
+ // register named Growl notification for new mail alerts.
+ if (!strcmp(aTopic, "before-growl-registration"))
+ {
+ printf("\n\n\nbefore-growl-registration\n\n\n");
+ nsresult rv;
+ nsCOMPtr<nsIObserverService> observerService = do_GetService("@mozilla.org/observer-service;1", &rv);
+ if (NS_SUCCEEDED(rv))
+ observerService->RemoveObserver(this, "before-growl-registration");
+
+ nsCOMPtr<nsINotificationsList> notifications = do_QueryInterface(aSubject, &rv);
+ if (NS_SUCCEEDED(rv))
+ {
+ printf("...trying to get string bundle...\n");
+ nsCOMPtr<nsIStringBundle> bundle;
+ GetStringBundle(getter_AddRefs(bundle));
+ if (bundle)
+ {
+ nsString growlNotification;
+ bundle->GetStringFromName(NS_LITERAL_STRING("growlNotification").get(), getter_Copies(growlNotification));
+ notifications->AddNotification(growlNotification, PR_TRUE);
+ }
+ }
+ }
+ return NS_OK;
+}
+
+PRInt32
+nsMessengerOSXIntegration::CountNewMessages()
{
// iterate over all the folders in mFoldersWithNewMail
nsCOMPtr<nsIMsgFolder> folder;
nsCOMPtr<nsIWeakReference> weakReference;
PRInt32 numNewMessages = 0;
PRInt32 totalNewMessages = 0;
PRUint32 count = 0;
mFoldersWithNewMail->Count(&count);
for (PRUint32 index = 0; index < count; index++)
{
weakReference = do_QueryElementAt(mFoldersWithNewMail, index);
folder = do_QueryReferent(weakReference);
if (folder)
{
-
numNewMessages = 0;
folder->GetNumNewMessages(PR_TRUE, &numNewMessages);
totalNewMessages += numNewMessages;
} // if we got a folder
} // for each folder
return totalNewMessages;
-
+}
+
+nsresult
+nsMessengerOSXIntegration::GetStringBundle(nsIStringBundle **aBundle)
+{
+ NS_ENSURE_ARG_POINTER(aBundle);
+ nsresult rv;
+ nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
+ nsCOMPtr<nsIStringBundle> bundle;
+ if (bundleService && NS_SUCCEEDED(rv))
+ bundleService->CreateBundle("chrome://messenger/locale/messenger.properties", getter_AddRefs(bundle));
+ bundle.swap(*aBundle);
+ return rv;
+}
+
+void
+nsMessengerOSXIntegration::FillToolTipInfo()
+{
+ nsCOMPtr<nsIWeakReference> weakReference = do_QueryElementAt(mFoldersWithNewMail, 0);
+ nsCOMPtr<nsIMsgFolder> folder = do_QueryReferent(weakReference);
+ if (folder)
+ {
+ nsString accountName;
+ folder->GetPrettiestName(accountName);
+
+ nsCOMPtr<nsIStringBundle> bundle;
+ GetStringBundle(getter_AddRefs(bundle));
+ if (bundle)
+ {
+ PRInt32 numNewMessages = 0;
+ folder->GetNumNewMessages(PR_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
+ } // if we got a folder
}
-nsresult nsMessengerOSXIntegration::OnAlertFinished(const PRUnichar * aAlertCookie)
+nsresult
+nsMessengerOSXIntegration::ShowAlertMessage(const nsAString& aAlertTitle,
+ const nsAString& aAlertText,
+ const nsACString& aFolderURI)
+{
+ // if we are alredy in the process of showing an alert, don't try to show another one
+ if (mAlertInProgress)
+ return NS_OK;
+
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ PRBool showAlert = PR_TRUE;
+ prefBranch->GetBoolPref(kBiffShowAlertPref, &showAlert);
+
+ if (showAlert)
+ {
+ nsCOMPtr<nsIAlertsService> alertsService (do_GetService(NS_ALERTSERVICE_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsIStringBundle> bundle;
+ GetStringBundle(getter_AddRefs(bundle));
+ if (bundle)
+ {
+ nsString growlNotification;
+ bundle->GetStringFromName(NS_LITERAL_STRING("growlNotification").get(), getter_Copies(growlNotification));
+ rv = alertsService->ShowAlertNotification(NS_LITERAL_STRING(kNewMailAlertIcon), aAlertTitle,
+ aAlertText, PR_TRUE, NS_ConvertASCIItoUTF16(aFolderURI),
+ this, growlNotification);
+ mAlertInProgress = PR_TRUE;
+ }
+ }
+ }
+
+ if (!showAlert || NS_FAILED(rv))
+ OnAlertFinished(nsnull);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMessengerOSXIntegration::OnItemIntPropertyChanged(nsIMsgFolder *aFolder,
+ nsIAtom *aProperty,
+ PRInt32 aOldValue,
+ PRInt32 aNewValue)
{
- nsresult rv = NS_OK;
+ // if we got new mail show an alert
+ if (mBiffStateAtom == aProperty && mFoldersWithNewMail)
+ {
+ NS_ENSURE_TRUE(aFolder, NS_OK);
+
+ if (aNewValue == nsIMsgFolder::nsMsgBiffState_NewMail)
+ {
+ // if the icon is not already visible, only show a system tray icon if
+ // we are performing biff (as opposed to the user getting new mail)
+ if (!mBiffIconVisible)
+ {
+ PRBool performingBiff = PR_FALSE;
+ nsCOMPtr<nsIMsgIncomingServer> server;
+ aFolder->GetServer(getter_AddRefs(server));
+ if (server)
+ server->GetPerformingBiff(&performingBiff);
+ if (!performingBiff)
+ return NS_OK; // kick out right now...
+ }
+
+ nsCOMPtr<nsIWeakReference> weakFolder = do_GetWeakReference(aFolder);
+
+ if (mFoldersWithNewMail->IndexOf(weakFolder) == kNotFound)
+ mFoldersWithNewMail->AppendElement(weakFolder);
+
+ FillToolTipInfo();
+ }
+ else if (aNewValue == nsIMsgFolder::nsMsgBiffState_NoMail)
+ {
+ // we are always going to remove the icon whenever we get our first no
+ // mail notification.
+ mFoldersWithNewMail->Clear();
+ if (mBiffIconVisible)
+ {
+ RestoreApplicationDockTileImage();
+ mBiffIconVisible = PR_FALSE;
+ }
+ }
+ } // if the biff property changed
+
+ return NS_OK;
+}
+
+nsresult
+nsMessengerOSXIntegration::OnAlertClicked()
+{
+#ifdef MOZ_THUNDERBIRD
+ nsresult rv;
+ nsCOMPtr<nsIMsgMailSession> mailSession = do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr<nsIMsgWindow> topMostMsgWindow;
+ rv = mailSession->GetTopmostMsgWindow(getter_AddRefs(topMostMsgWindow));
+ if (topMostMsgWindow)
+ {
+ nsCOMPtr<nsIDOMWindowInternal> domWindow;
+ rv = topMostMsgWindow->GetDomWindow(getter_AddRefs(domWindow));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ domWindow->Focus();
+ }
+#else
+ nsCString folderURI;
+ GetFirstFolderWithNewMail(folderURI);
+ openMailWindow(folderURI);
+#endif
+ return NS_OK;
+}
+
+nsresult
+nsMessengerOSXIntegration::OnAlertFinished(const PRUnichar * aAlertCookie)
+{
+ nsresult rv;
nsCOMPtr<nsIPrefBranch> prefBranch (do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
NS_ENSURE_SUCCESS(rv, rv);
PRBool bounceDockIcon = PR_FALSE;
prefBranch->GetBoolPref("mail.biff.animate_dock_icon", &bounceDockIcon);
// This will call GetAttention(), which will bounce the dock icon.
if (!mSuppressBiffIcon)
@@ -195,17 +440,16 @@ nsresult nsMessengerOSXIntegration::OnAl
// use OverlayApplicationDockTileImage
// -- you'll have to pass it a CGImage, and somehow we have to
// create the CGImage with the numbers. tricky
PRInt32 totalNewMessages = CountNewMessages();
CGContextRef context = ::BeginCGContextForApplicationDockTile();
// Draw a circle.
-
::CGContextBeginPath(context);
::CGContextAddArc(context, 95.0, 95.0, 25.0, 0.0, 2 * M_PI, true);
::CGContextClosePath(context);
// use #2fc600 for the color.
::CGContextSetRGBFillColor(context, 0.184, 0.776, 0.0, 1);
//::CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 0.7);
@@ -249,62 +493,60 @@ nsresult nsMessengerOSXIntegration::OnAl
ATSUAttributeTag tags[3] = { kATSUFontTag, kATSUSizeTag, kATSUColorTag };
ByteCount valueSizes[3] = { sizeof(ATSUFontID), sizeof(Fixed),
sizeof(RGBColor) };
ATSUAttributeValuePtr values[3] = { &fmFont, &size, &white };
err = ::ATSUSetAttributes(style, 3, tags, valueSizes, values);
if (err != noErr) {
- NS_WARNING("ATSUSetAttributes failed");
- ::ATSUDisposeStyle(style);
- ::EndCGContextForApplicationDockTile(context);
-
- return NS_ERROR_FAILURE;
- }
+ NS_WARNING("ATSUSetAttributes failed");
+ ::ATSUDisposeStyle(style);
+ ::EndCGContextForApplicationDockTile(context);
+ return NS_ERROR_FAILURE;
+ }
UniCharCount runLengths = kATSUToTextEnd;
ATSUTextLayout textLayout;
err = ::ATSUCreateTextLayoutWithTextPtr(total.get(),
kATSUFromTextBeginning,
kATSUToTextEnd, total.Length(), 1,
&runLengths, &style, &textLayout);
if (err != noErr)
{
- NS_WARNING("ATSUCreateTextLayoutWithTextPtr failed");
- ::ATSUDisposeStyle(style);
- ::EndCGContextForApplicationDockTile(context);
-
- return NS_ERROR_FAILURE;
+ NS_WARNING("ATSUCreateTextLayoutWithTextPtr failed");
+ ::ATSUDisposeStyle(style);
+ ::EndCGContextForApplicationDockTile(context);
+ return NS_ERROR_FAILURE;
}
ATSUAttributeTag layoutTags[1] = { kATSUCGContextTag };
ByteCount layoutValueSizes[1] = { sizeof(CGContextRef) };
ATSUAttributeValuePtr layoutValues[1] = { &context };
err = ::ATSUSetLayoutControls(textLayout, 1, layoutTags, layoutValueSizes,
layoutValues);
if (err != noErr)
{
- NS_WARNING("ATSUSetLayoutControls failed");
- ::ATSUDisposeStyle(style);
- ::EndCGContextForApplicationDockTile(context);
- return NS_ERROR_FAILURE;
+ NS_WARNING("ATSUSetLayoutControls failed");
+ ::ATSUDisposeStyle(style);
+ ::EndCGContextForApplicationDockTile(context);
+ return NS_ERROR_FAILURE;
}
Rect boundingBox;
err = ::ATSUMeasureTextImage(textLayout, kATSUFromTextBeginning,
kATSUToTextEnd, Long2Fix(0), Long2Fix(0),
&boundingBox);
if (err != noErr)
{
- NS_WARNING("ATSUMeasureTextImage failed");
- ::ATSUDisposeStyle(style);
- ::EndCGContextForApplicationDockTile(context);
- return NS_ERROR_FAILURE;
+ NS_WARNING("ATSUMeasureTextImage failed");
+ ::ATSUDisposeStyle(style);
+ ::EndCGContextForApplicationDockTile(context);
+ return NS_ERROR_FAILURE;
}
// Center text inside circle
err = ::ATSUDrawText(textLayout, kATSUFromTextBeginning, kATSUToTextEnd,
Long2Fix(90 - (boundingBox.right - boundingBox.left) / 2),
Long2Fix(95 - (boundingBox.bottom - boundingBox.top) / 2));
::ATSUDisposeStyle(style);
@@ -313,17 +555,17 @@ nsresult nsMessengerOSXIntegration::OnAl
::CGContextFlush(context);
::EndCGContextForApplicationDockTile(context);
mBiffIconVisible = PR_TRUE;
}
mSuppressBiffIcon = PR_FALSE;
mAlertInProgress = PR_FALSE;
- return NS_OK;
+ return rv; // was NS_OK;
}
NS_IMETHODIMP
nsMessengerOSXIntegration::OnItemPropertyFlagChanged(nsIMsgDBHdr *item, nsIAtom *property, PRUint32 oldFlag, PRUint32 newFlag)
{
return NS_OK;
}
@@ -343,67 +585,68 @@ nsMessengerOSXIntegration::OnItemBoolPro
}
NS_IMETHODIMP
nsMessengerOSXIntegration::OnItemEvent(nsIMsgFolder *, nsIAtom *)
{
return NS_OK;
}
-NS_IMETHODIMP
-nsMessengerOSXIntegration::OnItemIntPropertyChanged(nsIMsgFolder *aItem, nsIAtom *aProperty, PRInt32 aOldValue, PRInt32 aNewValue)
+// get the first top level folder which we know has new mail, then enumerate over all the subfolders
+// looking for the first real folder with new mail. Return the folderURI for that folder.
+nsresult
+nsMessengerOSXIntegration::GetFirstFolderWithNewMail(nsACString& aFolderURI)
{
- // if we got new mail bounce the Dock icon and/or apply badge to Dock icon
- if (mBiffStateAtom == aProperty && mFoldersWithNewMail)
- {
- if (aNewValue == nsIMsgFolder::nsMsgBiffState_NewMail)
- {
- // if the icon is not already visible, only show a system tray icon iff
- // we are performing biff (as opposed to the user getting new mail)
- if (!mBiffIconVisible)
- {
- PRBool performingBiff = PR_FALSE;
- nsCOMPtr<nsIMsgIncomingServer> server;
- aItem->GetServer(getter_AddRefs(server));
- if (server)
- server->GetPerformingBiff(&performingBiff);
- if (!performingBiff)
- return NS_OK; // kick out right now...
- }
+ nsresult rv;
+ NS_ENSURE_TRUE(mFoldersWithNewMail, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsIMsgFolder> folder;
+ nsCOMPtr<nsIWeakReference> weakReference;
+ PRInt32 numNewMessages = 0;
+
+ PRUint32 count = 0;
+ mFoldersWithNewMail->Count(&count);
+
+ if (!count) // kick out if we don't have any folders with new mail
+ return NS_OK;
+
+ weakReference = do_QueryElementAt(mFoldersWithNewMail, 0);
+ folder = do_QueryReferent(weakReference);
- nsCOMPtr<nsIWeakReference> weakFolder = do_GetWeakReference(aItem);
- // remove the element if it is already in the array....
- PRUint32 count = 0;
- PRUint32 index = 0;
- mFoldersWithNewMail->Count(&count);
- nsCOMPtr<nsIMsgFolder> oldFolder;
- nsCOMPtr<nsIWeakReference> weakReference;
- for (index = 0; index < count; index++)
- {
- weakReference = do_QueryElementAt(mFoldersWithNewMail, index);
- oldFolder = do_QueryReferent(weakReference);
- if (oldFolder == aItem) // if they point to the same folder
- break;
- oldFolder = nsnull;
- }
+ if (folder)
+ {
+ nsCOMPtr<nsIMsgFolder> msgFolder;
+ // enumerate over the folders under this root folder till we find one with new mail....
+ nsCOMPtr<nsISupportsArray> allFolders;
+ NS_NewISupportsArray(getter_AddRefs(allFolders));
+ rv = folder->ListDescendents(allFolders);
+ NS_ENSURE_SUCCESS(rv, rv);
- if (oldFolder)
- mFoldersWithNewMail->ReplaceElementAt(weakFolder, index);
- else
- mFoldersWithNewMail->AppendElement(weakFolder);
- // now regenerate the tooltip
- OnAlertFinished(nsnull);
- }
- else if (aNewValue == nsIMsgFolder::nsMsgBiffState_NoMail)
- {
- // we are always going to remove the icon whenever we get our first no mail
- // notification.
- mFoldersWithNewMail->Clear();
- if (mBiffIconVisible)
+ nsCOMPtr<nsIEnumerator> enumerator;
+ allFolders->Enumerate(getter_AddRefs(enumerator));
+ if (enumerator)
+ {
+ nsCOMPtr<nsISupports> supports;
+ nsresult more = enumerator->First();
+ while (NS_SUCCEEDED(more))
{
- RestoreApplicationDockTileImage();
- mBiffIconVisible = PR_FALSE;
- }
- }
- } // if the biff property changed
-
+ rv = enumerator->CurrentItem(getter_AddRefs(supports));
+ if (supports)
+ {
+ msgFolder = do_QueryInterface(supports, &rv);
+ if (msgFolder)
+ {
+ numNewMessages = 0;
+ msgFolder->GetNumNewMessages(PR_FALSE, &numNewMessages);
+ if (numNewMessages)
+ break; // kick out of the while loop
+ more = enumerator->Next();
+ }
+ } // if we have a folder
+ } // if we have more potential folders to enumerate
+ } // if enumerator
+
+ if (msgFolder)
+ msgFolder->GetURI(aFolderURI);
+ }
+
return NS_OK;
}