Bug 308552 Growl Integration for Mail Alerts on Mac OS X. p=David Humphrey <david.humphrey@senecac.on.ca>,r/sr=bienvenu

This commit is contained in:
bugzilla%standard8.plus.com 2008-06-26 08:53:00 +00:00
Родитель 75220aef73
Коммит b74853612b
5 изменённых файлов: 277 добавлений и 92 удалений

Просмотреть файл

@ -447,6 +447,7 @@ alwaysLoadRemoteContentForSender1= Always load remote images from %1$S
# Strings for growl notifications on Mac OS X
subjectNotificationTitle=Subject: "%1$S"
senderNotificationText=Sender: "%1$S"
growlNotification=New Mail
# mailCommands.js
emptyJunkTitle=Confirm

Просмотреть файл

@ -85,6 +85,10 @@ REQUIRES = xpcom \
webbrwsr \
$(NULL)
ifeq ($(OS_TARGET),Darwin)
REQUIRES += alerts
endif
CPPSRCS = nsMsgFactory.cpp
SHARED_LIBRARY_LIBS = \

Просмотреть файл

@ -65,11 +65,54 @@
#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"
#define kBiffBadgeIcon "mail-biff-badge.png"
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");
@ -96,14 +139,19 @@ 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);
@ -116,10 +164,7 @@ nsMessengerOSXIntegration::Init()
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
@ -140,6 +185,39 @@ nsMessengerOSXIntegration::OnItemRemoved(nsIRDFResource *, nsISupports *)
return NS_OK;
}
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"))
{
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))
{
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
@ -157,7 +235,6 @@ PRInt32 nsMessengerOSXIntegration::CountNewMessages()
folder = do_QueryReferent(weakReference);
if (folder)
{
numNewMessages = 0;
folder->GetNumNewMessages(PR_TRUE, &numNewMessages);
totalNewMessages += numNewMessages;
@ -165,12 +242,167 @@ PRInt32 nsMessengerOSXIntegration::CountNewMessages()
} // 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::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(nsIRDFResource *aItem, nsIAtom *aProperty, PRInt32 aOldValue, PRInt32 aNewValue)
{
// if we got new mail show an alert
if (mBiffStateAtom == aProperty && mFoldersWithNewMail)
{
nsCOMPtr<nsIMsgFolder> folder = do_QueryInterface(aItem);
NS_ENSURE_TRUE(folder, NS_OK);
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;
folder->GetServer(getter_AddRefs(server));
if (server)
server->GetPerformingBiff(&performingBiff);
if (!performingBiff)
return NS_OK; // kick out right now...
}
nsCOMPtr<nsIWeakReference> weakFolder = do_GetWeakReference(folder);
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 = NS_OK;
nsresult rv;
nsCOMPtr<nsIPrefBranch> prefBranch (do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
NS_ENSURE_SUCCESS(rv, rv);
@ -203,7 +435,6 @@ nsresult nsMessengerOSXIntegration::OnAlertFinished(const PRUnichar * aAlertCook
CGContextRef context = ::BeginCGContextForApplicationDockTile();
// Draw a circle.
::CGContextBeginPath(context);
::CGContextAddArc(context, 95.0, 95.0, 25.0, 0.0, 2 * M_PI, true);
::CGContextClosePath(context);
@ -257,12 +488,11 @@ nsresult nsMessengerOSXIntegration::OnAlertFinished(const PRUnichar * aAlertCook
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;
@ -272,11 +502,10 @@ nsresult nsMessengerOSXIntegration::OnAlertFinished(const PRUnichar * aAlertCook
&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 };
@ -287,10 +516,10 @@ nsresult nsMessengerOSXIntegration::OnAlertFinished(const PRUnichar * aAlertCook
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;
@ -299,10 +528,10 @@ nsresult nsMessengerOSXIntegration::OnAlertFinished(const PRUnichar * aAlertCook
&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
@ -321,7 +550,7 @@ nsresult nsMessengerOSXIntegration::OnAlertFinished(const PRUnichar * aAlertCook
mSuppressBiffIcon = PR_FALSE;
mAlertInProgress = PR_FALSE;
return NS_OK;
return rv; // was NS_OK;
}
NS_IMETHODIMP
@ -350,66 +579,3 @@ nsMessengerOSXIntegration::OnItemEvent(nsIMsgFolder *, nsIAtom *)
{
return NS_OK;
}
NS_IMETHODIMP
nsMessengerOSXIntegration::OnItemIntPropertyChanged(nsIRDFResource *aItem, nsIAtom *aProperty, PRInt32 aOldValue, PRInt32 aNewValue)
{
// if we got new mail bounce the Dock icon and/or apply badge to Dock icon
if (mBiffStateAtom == aProperty && mFoldersWithNewMail)
{
nsCOMPtr<nsIMsgFolder> folder = do_QueryInterface(aItem);
NS_ENSURE_TRUE(folder, NS_OK);
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;
folder->GetServer(getter_AddRefs(server));
if (server)
server->GetPerformingBiff(&performingBiff);
if (!performingBiff)
return NS_OK; // kick out right now...
}
nsCOMPtr<nsIWeakReference> weakFolder = do_GetWeakReference(folder);
// 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 == folder) // if they point to the same folder
break;
oldFolder = nsnull;
}
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)
{
RestoreApplicationDockTileImage();
mBiffIconVisible = PR_FALSE;
}
}
} // if the biff property changed
return NS_OK;
}

Просмотреть файл

@ -48,13 +48,18 @@
#include "nsString.h"
#include "nsInt64.h"
#include "nsISupportsArray.h"
#include "nsIObserver.h"
#include "nsIAlertsService.h"
#define NS_MESSENGEROSXINTEGRATION_CID \
{0xaa83266, 0x4225, 0x4c4b, \
{0x93, 0xf8, 0x94, 0xb1, 0x82, 0x58, 0x6f, 0x93}}
class nsIStringBundle;
class nsMessengerOSXIntegration : public nsIMessengerOSIntegration,
public nsIFolderListener
public nsIFolderListener,
public nsIObserver
{
public:
nsMessengerOSXIntegration();
@ -64,12 +69,17 @@ public:
NS_DECL_ISUPPORTS
NS_DECL_NSIMESSENGEROSINTEGRATION
NS_DECL_NSIFOLDERLISTENER
NS_DECL_NSIOBSERVER
private:
nsCOMPtr<nsISupportsArray> mFoldersWithNewMail; // keep track of all the root folders with pending new mail
nsCOMPtr<nsIAtom> mBiffStateAtom;
PRInt32 CountNewMessages();
nsresult ShowAlertMessage(const nsAString& aAlertTitle, const nsAString& aAlertText, const nsACString& aFolderURI);
nsresult OnAlertFinished(const PRUnichar * aAlertCookie);
nsresult OnAlertClicked();
nsresult GetStringBundle(nsIStringBundle **aBundle);
void FillToolTipInfo();
PRPackedBool mBiffIconVisible;
PRPackedBool mSuppressBiffIcon;

Просмотреть файл

@ -97,6 +97,10 @@ REQUIRES = xpcom \
uriloader \
$(NULL)
ifeq ($(OS_TARGET),Darwin)
REQUIRES += alerts
endif
ifdef MOZ_LDAP_XPCOM
REQUIRES += mozldap \
$(NULL)