diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /comm/mailnews/base/src/nsMessengerWinIntegration.cpp | |
parent | Initial commit. (diff) | |
download | thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'comm/mailnews/base/src/nsMessengerWinIntegration.cpp')
-rw-r--r-- | comm/mailnews/base/src/nsMessengerWinIntegration.cpp | 379 |
1 files changed, 379 insertions, 0 deletions
diff --git a/comm/mailnews/base/src/nsMessengerWinIntegration.cpp b/comm/mailnews/base/src/nsMessengerWinIntegration.cpp new file mode 100644 index 0000000000..7db74af5e0 --- /dev/null +++ b/comm/mailnews/base/src/nsMessengerWinIntegration.cpp @@ -0,0 +1,379 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +#include <windows.h> +#include <shellapi.h> +#include <strsafe.h> + +#include "mozilla/Components.h" +#include "mozilla/Services.h" +#include "mozIDOMWindow.h" +#include "nsIBaseWindow.h" +#include "nsIDocShell.h" +#include "nsIMsgWindow.h" +#include "nsIObserverService.h" +#include "nsIPrefService.h" +#include "nsIWidget.h" +#include "nsIWindowMediator.h" +#include "nsMessengerWinIntegration.h" +#include "nsMsgDBFolder.h" +#include "nsPIDOMWindow.h" + +#define IDI_MAILBIFF 32576 +#define SHOW_TRAY_ICON_PREF "mail.biff.show_tray_icon" +#define SHOW_TRAY_ICON_ALWAYS_PREF "mail.biff.show_tray_icon_always" + +// since we are including windows.h in this file, undefine get user name.... +#ifdef GetUserName +# undef GetUserName +#endif + +#ifndef NIIF_USER +# define NIIF_USER 0x00000004 +#endif + +#ifndef NIIF_NOSOUND +# define NIIF_NOSOUND 0x00000010 +#endif + +using namespace mozilla; + +nsMessengerWinIntegration::nsMessengerWinIntegration() {} + +nsMessengerWinIntegration::~nsMessengerWinIntegration() {} + +NS_IMPL_ADDREF(nsMessengerWinIntegration) +NS_IMPL_RELEASE(nsMessengerWinIntegration) + +NS_INTERFACE_MAP_BEGIN(nsMessengerWinIntegration) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIMessengerOSIntegration) + NS_INTERFACE_MAP_ENTRY(nsIMessengerWindowsIntegration) + NS_INTERFACE_MAP_ENTRY(nsIMessengerOSIntegration) +NS_INTERFACE_MAP_END + +static HWND hwndForDOMWindow(mozIDOMWindowProxy* window) { + if (!window) { + return 0; + } + nsCOMPtr<nsPIDOMWindowOuter> pidomwindow = nsPIDOMWindowOuter::From(window); + + nsCOMPtr<nsIBaseWindow> ppBaseWindow = + do_QueryInterface(pidomwindow->GetDocShell()); + if (!ppBaseWindow) return 0; + + nsCOMPtr<nsIWidget> ppWidget; + ppBaseWindow->GetMainWidget(getter_AddRefs(ppWidget)); + + return (HWND)(ppWidget->GetNativeData(NS_NATIVE_WIDGET)); +} + +static void activateWindow(mozIDOMWindowProxy* win) { + // Try to get native window handle. + HWND hwnd = hwndForDOMWindow(win); + if (hwnd) { + // Restore the window if it is minimized. + if (::IsIconic(hwnd)) ::ShowWindow(hwnd, SW_RESTORE); + // Use the OS call, if possible. + ::SetForegroundWindow(hwnd); + } else { + // Use internal method. + nsCOMPtr<nsPIDOMWindowOuter> privateWindow = nsPIDOMWindowOuter::From(win); + privateWindow->Focus(mozilla::dom::CallerType::System); + } +} + +NOTIFYICONDATAW sMailIconData = { + /* cbSize */ (DWORD)NOTIFYICONDATAW_V2_SIZE, + /* hWnd */ 0, + /* uID */ 2, + /* uFlags */ NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_INFO, + /* uCallbackMessage */ WM_USER, + /* hIcon */ 0, + /* szTip */ L"", + /* dwState */ 0, + /* dwStateMask */ 0, + /* szInfo */ L"", + /* uVersion */ {30000}, + /* szInfoTitle */ L"", + /* dwInfoFlags */ NIIF_USER | NIIF_NOSOUND}; + +static nsCOMArray<nsIBaseWindow> sHiddenWindows; +static HWND sIconWindow; +static uint32_t sUnreadCount; +/* static */ +LRESULT CALLBACK nsMessengerWinIntegration::IconWindowProc(HWND msgWindow, + UINT msg, WPARAM wp, + LPARAM lp) { + nsresult rv; + static UINT sTaskbarRecreated; + + switch (msg) { + case WM_USER: + if (msg == WM_USER && lp == WM_LBUTTONDOWN) { + nsCOMPtr<nsIPrefBranch> prefBranch = + do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, FALSE); + bool showTrayIcon; + rv = prefBranch->GetBoolPref(SHOW_TRAY_ICON_PREF, &showTrayIcon); + NS_ENSURE_SUCCESS(rv, FALSE); + bool showTrayIconAlways; + if (NS_FAILED(prefBranch->GetBoolPref(SHOW_TRAY_ICON_ALWAYS_PREF, + &showTrayIconAlways))) { + showTrayIconAlways = false; + } + if ((!showTrayIcon || !sUnreadCount) && !showTrayIconAlways) { + ::Shell_NotifyIconW(NIM_DELETE, &sMailIconData); + if (auto instance = reinterpret_cast<nsMessengerWinIntegration*>( + ::GetWindowLongPtrW(msgWindow, GWLP_USERDATA))) { + instance->mTrayIconShown = false; + } + } + + // No minimzed window, bring the most recent 3pane window to the front. + if (sHiddenWindows.Length() == 0) { + nsCOMPtr<nsIWindowMediator> windowMediator = + do_GetService(NS_WINDOWMEDIATOR_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, FALSE); + + nsCOMPtr<mozIDOMWindowProxy> domWindow; + rv = windowMediator->GetMostRecentBrowserWindow( + getter_AddRefs(domWindow)); + NS_ENSURE_SUCCESS(rv, FALSE); + if (domWindow) { + activateWindow(domWindow); + return TRUE; + } + } + + // Bring the minimized windows to the front. + for (uint32_t i = 0; i < sHiddenWindows.Length(); i++) { + auto window = sHiddenWindows.SafeElementAt(i); + if (!window) { + continue; + } + window->SetVisibility(true); + + nsCOMPtr<nsIWidget> widget; + window->GetMainWidget(getter_AddRefs(widget)); + if (!widget) { + continue; + } + + HWND hwnd = (HWND)(widget->GetNativeData(NS_NATIVE_WIDGET)); + ::ShowWindow(hwnd, SW_RESTORE); + ::SetForegroundWindow(hwnd); + + nsCOMPtr<nsIObserverService> obs = + mozilla::services::GetObserverService(); + obs->NotifyObservers(window, "windows-refresh-badge-tray", 0); + } + + sHiddenWindows.Clear(); + } + break; + case WM_CREATE: + sTaskbarRecreated = ::RegisterWindowMessageW(L"TaskbarCreated"); + break; + default: + if (msg == sTaskbarRecreated) { + // When taskbar is recreated (e.g. by restarting Windows Explorer), all + // tray icons are removed. If there are windows minimized to tray icon, + // we have to recreate the tray icon, otherwise the windows can't be + // restored. + if (auto instance = reinterpret_cast<nsMessengerWinIntegration*>( + ::GetWindowLongPtrW(msgWindow, GWLP_USERDATA))) { + instance->mTrayIconShown = false; + } + for (uint32_t i = 0; i < sHiddenWindows.Length(); i++) { + auto window = sHiddenWindows.SafeElementAt(i); + if (!window) { + continue; + } + nsCOMPtr<nsIObserverService> obs = + mozilla::services::GetObserverService(); + obs->NotifyObservers(window, "windows-refresh-badge-tray", 0); + } + } + break; + } + return ::DefWindowProc(msgWindow, msg, wp, lp); +} + +nsresult nsMessengerWinIntegration::HideWindow(nsIBaseWindow* aWindow) { + NS_ENSURE_ARG(aWindow); + aWindow->SetVisibility(false); + sHiddenWindows.AppendElement(aWindow); + + nsresult rv; + rv = CreateIconWindow(); + NS_ENSURE_SUCCESS(rv, rv); + + if (!mTrayIconShown) { + auto idi = IDI_APPLICATION; + if (sUnreadCount > 0) { + idi = MAKEINTRESOURCE(IDI_MAILBIFF); + } + sMailIconData.hIcon = ::LoadIcon(::GetModuleHandle(NULL), idi); + nsresult rv = SetTooltip(); + NS_ENSURE_SUCCESS(rv, rv); + + ::Shell_NotifyIconW(NIM_ADD, &sMailIconData); + ::Shell_NotifyIconW(NIM_SETVERSION, &sMailIconData); + mTrayIconShown = true; + } + return NS_OK; +} + +NS_IMETHODIMP +nsMessengerWinIntegration::ShowWindow(mozIDOMWindowProxy* aWindow) { + activateWindow(aWindow); + return NS_OK; +} + +NS_IMETHODIMP +nsMessengerWinIntegration::UpdateUnreadCount(uint32_t unreadCount, + const nsAString& unreadTooltip) { + sUnreadCount = unreadCount; + mUnreadTooltip = unreadTooltip; + nsresult rv = UpdateTrayIcon(); + return rv; +} + +NS_IMETHODIMP +nsMessengerWinIntegration::OnExit() { + if (mTrayIconShown) { + ::Shell_NotifyIconW(NIM_DELETE, &sMailIconData); + mTrayIconShown = false; + } + return NS_OK; +} + +/** + * Set a tooltip to the tray icon. Including the brand short name, and unread + * message count. + */ +nsresult nsMessengerWinIntegration::SetTooltip() { + nsresult rv = NS_OK; + if (mBrandShortName.IsEmpty()) { + nsCOMPtr<nsIStringBundleService> bundleService = + mozilla::components::StringBundle::Service(); + NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED); + nsCOMPtr<nsIStringBundle> bundle; + rv = bundleService->CreateBundle( + "chrome://branding/locale/brand.properties", getter_AddRefs(bundle)); + NS_ENSURE_SUCCESS(rv, rv); + rv = bundle->GetStringFromName("brandShortName", mBrandShortName); + NS_ENSURE_SUCCESS(rv, rv); + } + nsString tooltip = mBrandShortName; + if (!mUnreadTooltip.IsEmpty()) { + tooltip.AppendLiteral("\n"); + tooltip.Append(mUnreadTooltip); + } + size_t destLength = + sizeof sMailIconData.szTip / (sizeof sMailIconData.szTip[0]); + ::StringCchCopyNW(sMailIconData.szTip, destLength, tooltip.get(), + tooltip.Length()); + return rv; +} + +/** + * Create a custom window for the taskbar icon if it's not created yet. + */ +nsresult nsMessengerWinIntegration::CreateIconWindow() { + if (sMailIconData.hWnd) { + return NS_OK; + } + + const wchar_t kClassName[] = L"IconWindowClass"; + WNDCLASS classStruct = {/* style */ 0, + /* lpfnWndProc */ &IconWindowProc, + /* cbClsExtra */ 0, + /* cbWndExtra */ 0, + /* hInstance */ 0, + /* hIcon */ 0, + /* hCursor */ 0, + /* hbrBackground */ 0, + /* lpszMenuName */ 0, + /* lpszClassName */ kClassName}; + + // Register the window class. + NS_ENSURE_TRUE(::RegisterClass(&classStruct), NS_ERROR_FAILURE); + // Create the window. + NS_ENSURE_TRUE(sIconWindow = ::CreateWindow( + /* className */ kClassName, + /* title */ 0, + /* style */ WS_CAPTION, + /* x, y, cx, cy */ 0, 0, 0, 0, + /* parent */ 0, + /* menu */ 0, + /* instance */ 0, + /* create struct */ 0), + NS_ERROR_FAILURE); + NS_ENSURE_TRUE(::SetWindowLongPtrW(sIconWindow, GWLP_USERDATA, + reinterpret_cast<LONG_PTR>(this)) == 0, + NS_ERROR_FAILURE); + + sMailIconData.hWnd = sIconWindow; + return NS_OK; +} + +/** + * Update the tray icon according to the current unread count and pref value. + */ +nsresult nsMessengerWinIntegration::UpdateTrayIcon() { + nsresult rv; + + rv = CreateIconWindow(); + NS_ENSURE_SUCCESS(rv, rv); + + if (!mPrefBranch) { + mPrefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + } + + rv = SetTooltip(); + NS_ENSURE_SUCCESS(rv, rv); + + bool showTrayIconAlways; + if (NS_FAILED(mPrefBranch->GetBoolPref(SHOW_TRAY_ICON_ALWAYS_PREF, + &showTrayIconAlways))) { + showTrayIconAlways = false; + } + if (sUnreadCount > 0 || showTrayIconAlways) { + auto idi = IDI_APPLICATION; + if (sUnreadCount > 0) { + // Only showing the new mail marker when there are actual unread mail + idi = MAKEINTRESOURCE(IDI_MAILBIFF); + } + sMailIconData.hIcon = ::LoadIcon(::GetModuleHandle(NULL), idi); + if (mTrayIconShown) { + // If the tray icon is already shown, just modify it. + ::Shell_NotifyIconW(NIM_MODIFY, &sMailIconData); + } else { + bool showTrayIcon; + rv = mPrefBranch->GetBoolPref(SHOW_TRAY_ICON_PREF, &showTrayIcon); + NS_ENSURE_SUCCESS(rv, rv); + if (showTrayIcon) { + // Show a tray icon only if the pref value is true. + ::Shell_NotifyIconW(NIM_ADD, &sMailIconData); + ::Shell_NotifyIconW(NIM_SETVERSION, &sMailIconData); + mTrayIconShown = true; + } + } + } else if (mTrayIconShown) { + if (sHiddenWindows.Length() > 0) { + // At least one window is minimized, modify the icon only. + sMailIconData.hIcon = + ::LoadIcon(::GetModuleHandle(NULL), IDI_APPLICATION); + ::Shell_NotifyIconW(NIM_MODIFY, &sMailIconData); + } else if (!showTrayIconAlways) { + // No unread, no need to show the tray icon. + ::Shell_NotifyIconW(NIM_DELETE, &sMailIconData); + mTrayIconShown = false; + } + } + return rv; +} |