Bug #119344 --> add windows system tray icon for biff

r=bhuvan
sr=sspitzer
This commit is contained in:
mscott%netscape.com 2002-01-25 22:16:01 +00:00
parent 8b34ef9d21
commit e2d2db6ed1
2 changed files with 250 additions and 16 deletions

View File

@ -38,6 +38,7 @@
* ***** END LICENSE BLOCK ***** */
#include <windows.h>
#include <shellapi.h>
#include "nsMessengerWinIntegration.h"
#include "nsIMsgAccountManager.h"
@ -54,6 +55,16 @@
#include "nsIProfile.h"
#include "nsIDirectoryService.h"
#include "nsIDOMWindowInternal.h"
#include "nsIScriptGlobalObject.h"
#include "nsIDocShell.h"
#include "nsIBaseWindow.h"
#include "nsIWidget.h"
#include "nsIAppShellService.h"
#include "prprf.h"
#include "nsIWeakReference.h"
#include "nsIStringBundle.h"
#define XP_SHSetUnreadMailCounts "SHSetUnreadMailCountW"
#define XP_SHEnumerateUnreadMailAccounts "SHEnumerateUnreadMailAccountsW"
#define UNREADMAILNODEKEY "Software\\Microsoft\\Windows\\CurrentVersion\\UnreadMail\\"
@ -62,6 +73,12 @@
#define PROFILE_COMMANDLINE_ARG " -p "
#define MAIL_COMMANDLINE_ARG " -mail"
#define TIMER_INTERVAL_PREF "mail.windows_xp_integration.unread_count_interval"
#define IDI_MAILBIFF 101
// since we are including windows.h in this file, undefine get user name....
#ifdef GetUserName
#undef GetUserName
#endif
nsMessengerWinIntegration::nsMessengerWinIntegration()
{
@ -73,6 +90,13 @@ nsMessengerWinIntegration::nsMessengerWinIntegration()
mFirstTimeFolderUnreadCountChanged = PR_TRUE;
mInboxURI = nsnull;
mEmail = nsnull;
mStoreUnreadCounts = PR_FALSE;
mIntervalTime = 0;
mBiffStateAtom = getter_AddRefs(NS_NewAtom("BiffState"));
mBiffIconVisible = PR_FALSE;
NS_NewISupportsArray(getter_AddRefs(mFoldersWithNewMail));
}
nsMessengerWinIntegration::~nsMessengerWinIntegration()
@ -88,6 +112,12 @@ nsMessengerWinIntegration::~nsMessengerWinIntegration()
CRTFREEIF(mInboxURI);
CRTFREEIF(mEmail);
if (mBiffIconData.hIcon)
{
::Shell_NotifyIconW( NIM_DELETE, &mBiffIconData ); // Remove the tray icon.
DestroyIcon(mBiffIconData.hIcon);
}
}
NS_IMPL_ADDREF(nsMessengerWinIntegration);
@ -110,15 +140,62 @@ nsMessengerWinIntegration::ResetCurrent()
mLastUnreadCountWrittenToRegistry = -1;
mDefaultAccountMightHaveAnInbox = PR_TRUE;
return NS_OK;
}
// shamelessly ripped directly from nsNativeAppSupportWin.cpp
HWND hwndForDOMWindow( nsISupports *window )
{
nsCOMPtr<nsIScriptGlobalObject> ppScriptGlobalObj( do_QueryInterface(window) );
if ( !ppScriptGlobalObj )
return 0;
nsCOMPtr<nsIDocShell> ppDocShell;
ppScriptGlobalObj->GetDocShell( getter_AddRefs( ppDocShell ) );
if ( !ppDocShell )
return 0;
nsCOMPtr<nsIBaseWindow> ppBaseWindow( do_QueryInterface( ppDocShell ) );
if ( !ppBaseWindow )
return 0;
nsCOMPtr<nsIWidget> ppWidget;
ppBaseWindow->GetMainWidget( getter_AddRefs( ppWidget ) );
return (HWND)( ppWidget->GetNativeData( NS_NATIVE_WIDGET ) );
}
// end shameless copying from nsNativeAppSupportWin.cpp
NOTIFYICONDATAW nsMessengerWinIntegration::mBiffIconData = { sizeof(NOTIFYICONDATA),
0,
2,
NIF_ICON | NIF_MESSAGE | NIF_TIP,
WM_USER,
0,
0 };
void nsMessengerWinIntegration::InitializeBiffStatusIcon()
{
// initialize our biff status bar icon
nsresult rv = NS_OK;
nsCOMPtr<nsIAppShellService> appService = do_GetService( "@mozilla.org/appshell/appShellService;1", &rv);
if (NS_FAILED(rv)) return;
nsCOMPtr<nsIDOMWindowInternal> hiddenWindow;
rv = appService->GetHiddenDOMWindow(getter_AddRefs(hiddenWindow));
mBiffIconData.hWnd = hwndForDOMWindow( hiddenWindow );
mBiffIconData.hIcon = ::LoadIcon( ::GetModuleHandle( "msgbase.dll" ), MAKEINTRESOURCE(IDI_MAILBIFF) );
mBiffIconData.szTip[0] = 0;
}
nsresult
nsMessengerWinIntegration::Init()
{
nsresult rv;
InitializeBiffStatusIcon();
// get pref service
nsCOMPtr<nsIPref> prefService;
prefService = do_GetService(NS_PREF_CONTRACTID, &rv);
@ -129,10 +206,8 @@ nsMessengerWinIntegration::Init()
prefService->GetIntPref(TIMER_INTERVAL_PREF, &timerInterval);
// return if the timer value is negative or ZERO
if (timerInterval <= 0) {
return NS_OK;
}
else {
if (timerInterval > 0)
{
// set the interval for timer.
// multiply the value extracted (in seconds) from prefs
// with 1000 to convert the interval into milliseconds
@ -172,9 +247,9 @@ nsMessengerWinIntegration::Init()
}
// if failed to get either of the process addresses, this is not XP platform
// time to erturn.
if (!mSHSetUnreadMailCount || !mSHEnumerateUnreadMailAccounts)
return NS_OK;
// so we aren't storing unread counts
if (mSHSetUnreadMailCount && mSHEnumerateUnreadMailAccounts && (PRUint32) mIntervalTime)
mStoreUnreadCounts = PR_TRUE;
nsCOMPtr <nsIMsgAccountManager> accountManager =
do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
@ -184,17 +259,17 @@ nsMessengerWinIntegration::Init()
rv = accountManager->AddRootFolderListener(this);
NS_ENSURE_SUCCESS(rv,rv);
nsCOMPtr<nsIMsgMailSession> mailSession =
do_GetService(NS_MSGMAILSESSION_CONTRACTID, &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);
if (mStoreUnreadCounts)
{
// get current profile name to fill in commandliner.
nsCOMPtr<nsIProfile> profileService =
do_GetService(NS_PROFILE_CONTRACTID, &rv);
nsCOMPtr<nsIProfile> profileService = do_GetService(NS_PROFILE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv,rv);
profileService->GetCurrentProfile(getter_Copies(mProfileName));
@ -210,7 +285,7 @@ nsMessengerWinIntegration::Init()
NS_ENSURE_SUCCESS(rv,rv);
rv = SetupUnreadCountUpdateTimer();
NS_ENSURE_SUCCESS(rv,rv);
}
return NS_OK;
}
@ -233,9 +308,144 @@ nsMessengerWinIntegration::OnItemRemoved(nsISupports *, nsISupports *, const cha
return NS_OK;
}
NS_IMETHODIMP
nsMessengerWinIntegration::OnItemPropertyFlagChanged(nsISupports *, nsIAtom *, PRUint32, PRUint32)
nsresult nsMessengerWinIntegration::GetStringBundle(nsIStringBundle **aBundle)
{
nsresult rv = NS_OK;
NS_ENSURE_ARG_POINTER(aBundle);
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));
NS_IF_ADDREF(*aBundle = bundle);
return rv;
}
void nsMessengerWinIntegration::FillToolTipInfo()
{
// iterate over all the folders in mFoldersWithNewMail
nsXPIDLCString userName;
nsXPIDLCString hostName;
nsAutoString toolTipText;
nsCOMPtr<nsISupports> supports;
nsCOMPtr<nsIMsgFolder> folder;
nsCOMPtr<nsIWeakReference> weakReference;
PRInt32 numNewMessages = 0;
PRUint32 count = 0;
mFoldersWithNewMail->Count(&count);
for (PRUint32 index = 0; index < count; index++)
{
supports = getter_AddRefs(mFoldersWithNewMail->ElementAt(index));
weakReference = do_QueryInterface(supports);
folder = do_QueryReferent(weakReference);
if (folder)
{
folder->GetUsername(getter_Copies(userName));
numNewMessages = 0;
folder->GetNumNewMessages(PR_TRUE, &numNewMessages);
nsCOMPtr<nsIStringBundle> bundle;
GetStringBundle(getter_AddRefs(bundle));
if (bundle)
{
nsXPIDLString messageText;
nsAutoString numNewMsgsText;
nsAutoString uniUsername;
uniUsername.AssignWithConversion(userName);
numNewMsgsText.AppendInt(numNewMessages);
// fetch the message vs. messages string
if (numNewMessages == 1)
bundle->GetStringFromName(NS_LITERAL_STRING("message").get(), getter_Copies(messageText));
else
bundle->GetStringFromName(NS_LITERAL_STRING("messages").get(), getter_Copies(messageText));
const PRUnichar *formatStrings[] =
{
uniUsername.get(),
numNewMsgsText.get(),
messageText
};
nsXPIDLString finalText;
bundle->FormatStringFromName(NS_LITERAL_STRING("biffNotification").get(), formatStrings, 3, getter_Copies(finalText));
// only add this new string if it will fit without truncation....
if ((sizeof mBiffIconData.szTip - 1) >= toolTipText.Length() + finalText.Length() + 2)
{
if (index > 0)
toolTipText.Append(NS_LITERAL_STRING("\n").get());
toolTipText.Append(finalText);
}
} // if we got a bundle
} // if we got a folder
} // for each folder
if (toolTipText.Length())
::wcsncpy( mBiffIconData.szTip, toolTipText.get(), sizeof mBiffIconData.szTip - 1 );
// load an icon in the system tray or if it's already there, fire a modified notification so the tooltip
// gets updated.
if (!mBiffIconVisible)
{
::Shell_NotifyIconW( NIM_ADD, &mBiffIconData );
mBiffIconVisible = PR_TRUE;
}
else
::Shell_NotifyIconW( NIM_MODIFY, &mBiffIconData );
}
NS_IMETHODIMP
nsMessengerWinIntegration::OnItemPropertyFlagChanged(nsISupports *item, nsIAtom *property, PRUint32 oldFlag, PRUint32 newFlag)
{
nsresult rv = NS_OK;
// if we got new mail show a icon in the system tray
if (mBiffStateAtom == property && mFoldersWithNewMail)
{
nsCOMPtr<nsIMsgFolder> folder = do_QueryInterface(item);
NS_ENSURE_TRUE(folder, NS_OK);
if (newFlag == nsIMsgFolder::nsMsgBiffState_NewMail)
{
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<nsISupports> supports;
nsCOMPtr<nsIMsgFolder> oldFolder;
nsCOMPtr<nsIWeakReference> weakReference;
for (index = 0; index < count; index++)
{
supports = getter_AddRefs(mFoldersWithNewMail->ElementAt(index));
weakReference = do_QueryInterface(supports);
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
FillToolTipInfo();
}
else if (newFlag == nsIMsgFolder::nsMsgBiffState_NoMail)
{
// we are always going to remove the icon whenever we get our first no mail
// notification.
mFoldersWithNewMail->Clear();
if (mBiffIconVisible)
{
mBiffIconVisible = PR_FALSE;
::Shell_NotifyIconW( NIM_DELETE, &mBiffIconData ); // Remove the tray icon.
}
}
} // if the biff property changed
return NS_OK;
}
@ -245,7 +455,6 @@ nsMessengerWinIntegration::OnItemAdded(nsISupports *, nsISupports *, const char
return NS_OK;
}
NS_IMETHODIMP
nsMessengerWinIntegration::OnItemBoolPropertyChanged(nsISupports *aItem,
nsIAtom *aProperty,
@ -287,6 +496,8 @@ nsMessengerWinIntegration::OnItemIntPropertyChanged(nsISupports *aItem, nsIAtom
{
nsresult rv;
if (!mStoreUnreadCounts) return NS_OK; // don't do anything here if we aren't storing unread counts...
if (aProperty == mTotalUnreadMessagesAtom) {
nsCOMPtr <nsIRDFResource> folderResource = do_QueryInterface(aItem, &rv);
NS_ENSURE_SUCCESS(rv,rv);
@ -332,6 +543,8 @@ nsMessengerWinIntegration::OnUnreadCountUpdateTimer(nsITimer *timer, void *osInt
nsresult
nsMessengerWinIntegration::RemoveCurrentFromRegistry()
{
if (!mStoreUnreadCounts) return NS_OK; // don't do anything here if we aren't storing unread counts...
// If Windows XP, open the registry and get rid of old account registry entries
// If there is a email prefix, get it and use it to build the registry key.
// Otherwise, just the email address will be the registry key.
@ -379,6 +592,8 @@ nsMessengerWinIntegration::RemoveCurrentFromRegistry()
nsresult
nsMessengerWinIntegration::UpdateRegistryWithCurrent()
{
if (!mStoreUnreadCounts) return NS_OK; // don't do anything here if we aren't storing unread counts...
if (!mInboxURI || !mEmail)
return NS_OK;
@ -429,6 +644,7 @@ nsresult
nsMessengerWinIntegration::SetupInbox()
{
nsresult rv;
if (!mStoreUnreadCounts) return NS_OK; // don't do anything here if we aren't storing unread counts...
// get default account
nsCOMPtr <nsIMsgAccountManager> accountManager =
@ -534,6 +750,7 @@ nsresult
nsMessengerWinIntegration::UpdateUnreadCount()
{
nsresult rv;
if (!mStoreUnreadCounts) return NS_OK; // don't do anything here if we aren't storing unread counts...
if (mDefaultAccountMightHaveAnInbox && !mInboxURI) {
rv = SetupInbox();
@ -549,6 +766,7 @@ nsMessengerWinIntegration::UpdateUnreadCount()
nsresult
nsMessengerWinIntegration::SetupUnreadCountUpdateTimer()
{
if (!mStoreUnreadCounts) return NS_OK; // don't do anything here if we aren't storing unread counts...
PRUint32 timeInMSUint32 = (PRUint32) mIntervalTime;
if (mUnreadCountUpdateTimer) {
mUnreadCountUpdateTimer->Cancel();

View File

@ -50,6 +50,7 @@
#include "nsString.h"
#include "nsIPref.h"
#include "nsInt64.h"
#include "nsISupportsArray.h"
// this function is exported by shell32.dll version 5.60 or later (Windows XP or greater)
extern "C"
@ -62,6 +63,8 @@ typedef HRESULT (__stdcall *fnSHEnumerateUnreadMailAccounts)(HKEY hKeyUser, DWOR
{0xf62f3d3a, 0x1dd1, 0x11b2, \
{0xa5, 0x16, 0xef, 0xad, 0xb1, 0x31, 0x61, 0x5c}}
class nsIStringBundle;
class nsMessengerWinIntegration : public nsIMessengerOSIntegration,
public nsIFolderListener
{
@ -75,6 +78,19 @@ public:
NS_DECL_NSIFOLDERLISTENER
private:
static NOTIFYICONDATAW mBiffIconData; // status bar icon for biff.
void InitializeBiffStatusIcon();
void FillToolTipInfo();
nsresult GetStringBundle(nsIStringBundle **aBundle);
PRBool mBiffIconVisible;
nsCOMPtr<nsISupportsArray> mFoldersWithNewMail; // keep track of all the root folders with pending new mail
nsCOMPtr<nsIAtom> mBiffStateAtom;
PRUint32 mCurrentBiffState;
PRBool mStoreUnreadCounts; // for windows XP, we do a lot of work to store the last unread count for the inbox
// this flag is set to true when we are doing that
nsresult ResetCurrent();
nsresult RemoveCurrentFromRegistry();
nsresult UpdateRegistryWithCurrent();