michael@0: /* vim: se cin sw=2 ts=2 et : */ michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "TaskbarTabPreview.h" michael@0: #include "nsWindowGfx.h" michael@0: #include "nsUXThemeData.h" michael@0: #include "WinUtils.h" michael@0: #include michael@0: michael@0: #define TASKBARPREVIEW_HWNDID L"TaskbarTabPreviewHwnd" michael@0: michael@0: namespace mozilla { michael@0: namespace widget { michael@0: michael@0: NS_IMPL_ISUPPORTS(TaskbarTabPreview, nsITaskbarTabPreview) michael@0: michael@0: const wchar_t *const kWindowClass = L"MozillaTaskbarPreviewClass"; michael@0: michael@0: TaskbarTabPreview::TaskbarTabPreview(ITaskbarList4 *aTaskbar, nsITaskbarPreviewController *aController, HWND aHWND, nsIDocShell *aShell) michael@0: : TaskbarPreview(aTaskbar, aController, aHWND, aShell), michael@0: mProxyWindow(nullptr), michael@0: mIcon(nullptr), michael@0: mRegistered(false) michael@0: { michael@0: WindowHook &hook = GetWindowHook(); michael@0: hook.AddMonitor(WM_WINDOWPOSCHANGED, MainWindowHook, this); michael@0: } michael@0: michael@0: TaskbarTabPreview::~TaskbarTabPreview() { michael@0: if (mIcon) { michael@0: ::DestroyIcon(mIcon); michael@0: mIcon = nullptr; michael@0: } michael@0: michael@0: // We need to ensure that proxy window disappears or else Bad Things happen. michael@0: if (mProxyWindow) michael@0: Disable(); michael@0: michael@0: NS_ASSERTION(!mProxyWindow, "Taskbar proxy window was not destroyed!"); michael@0: michael@0: if (IsWindowAvailable()) { michael@0: DetachFromNSWindow(); michael@0: } else { michael@0: mWnd = nullptr; michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: TaskbarTabPreview::ShowActive(bool active) { michael@0: NS_ASSERTION(mVisible && CanMakeTaskbarCalls(), "ShowActive called on invisible window or before taskbar calls can be made for this window"); michael@0: return FAILED(mTaskbar->SetTabActive(active ? mProxyWindow : nullptr, michael@0: mWnd, 0)) ? NS_ERROR_FAILURE : NS_OK; michael@0: } michael@0: michael@0: HWND & michael@0: TaskbarTabPreview::PreviewWindow() { michael@0: return mProxyWindow; michael@0: } michael@0: michael@0: nativeWindow michael@0: TaskbarTabPreview::GetHWND() { michael@0: return mProxyWindow; michael@0: } michael@0: michael@0: void michael@0: TaskbarTabPreview::EnsureRegistration() { michael@0: NS_ASSERTION(mVisible && CanMakeTaskbarCalls(), "EnsureRegistration called when it is not safe to do so"); michael@0: michael@0: (void) UpdateTaskbarProperties(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: TaskbarTabPreview::GetTitle(nsAString &aTitle) { michael@0: aTitle = mTitle; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: TaskbarTabPreview::SetTitle(const nsAString &aTitle) { michael@0: mTitle = aTitle; michael@0: return mVisible ? UpdateTitle() : NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: TaskbarTabPreview::SetIcon(imgIContainer *icon) { michael@0: HICON hIcon = nullptr; michael@0: if (icon) { michael@0: nsresult rv; michael@0: rv = nsWindowGfx::CreateIcon(icon, false, 0, 0, michael@0: nsWindowGfx::GetIconMetrics(nsWindowGfx::kSmallIcon), michael@0: &hIcon); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: if (mIcon) michael@0: ::DestroyIcon(mIcon); michael@0: mIcon = hIcon; michael@0: mIconImage = icon; michael@0: return mVisible ? UpdateIcon() : NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: TaskbarTabPreview::GetIcon(imgIContainer **icon) { michael@0: NS_IF_ADDREF(*icon = mIconImage); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: TaskbarTabPreview::Move(nsITaskbarTabPreview *aNext) { michael@0: if (aNext == this) michael@0: return NS_ERROR_INVALID_ARG; michael@0: mNext = aNext; michael@0: return CanMakeTaskbarCalls() ? UpdateNext() : NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: TaskbarTabPreview::UpdateTaskbarProperties() { michael@0: if (mRegistered) michael@0: return NS_OK; michael@0: michael@0: if (FAILED(mTaskbar->RegisterTab(mProxyWindow, mWnd))) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsresult rv = UpdateNext(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = TaskbarPreview::UpdateTaskbarProperties(); michael@0: mRegistered = true; michael@0: return rv; michael@0: } michael@0: michael@0: LRESULT michael@0: TaskbarTabPreview::WndProc(UINT nMsg, WPARAM wParam, LPARAM lParam) { michael@0: nsRefPtr kungFuDeathGrip(this); michael@0: switch (nMsg) { michael@0: case WM_CREATE: michael@0: TaskbarPreview::EnableCustomDrawing(mProxyWindow, true); michael@0: return 0; michael@0: case WM_CLOSE: michael@0: mController->OnClose(); michael@0: return 0; michael@0: case WM_ACTIVATE: michael@0: if (LOWORD(wParam) == WA_ACTIVE) { michael@0: // Activate the tab the user selected then restore the main window, michael@0: // keeping normal/max window state intact. michael@0: bool activateWindow; michael@0: nsresult rv = mController->OnActivate(&activateWindow); michael@0: if (NS_SUCCEEDED(rv) && activateWindow) { michael@0: nsWindow* win = WinUtils::GetNSWindowPtr(mWnd); michael@0: if (win) { michael@0: nsWindow * parent = win->GetTopLevelWindow(true); michael@0: if (parent) { michael@0: parent->Show(true); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: return 0; michael@0: case WM_GETICON: michael@0: return (LRESULT)mIcon; michael@0: case WM_SYSCOMMAND: michael@0: // Send activation events to the top level window and select the proper michael@0: // tab through the controller. michael@0: if (wParam == SC_RESTORE || wParam == SC_MAXIMIZE) { michael@0: bool activateWindow; michael@0: nsresult rv = mController->OnActivate(&activateWindow); michael@0: if (NS_SUCCEEDED(rv) && activateWindow) { michael@0: // Note, restoring an iconic, maximized window here will only michael@0: // activate the maximized window. This is not a bug, it's default michael@0: // windows behavior. michael@0: ::SendMessageW(mWnd, WM_SYSCOMMAND, wParam, lParam); michael@0: } michael@0: return 0; michael@0: } michael@0: // Forward everything else to the top level window. Do not forward michael@0: // close since that's intended for the tab. When the preview proxy michael@0: // closes, we'll close the tab above. michael@0: return wParam == SC_CLOSE michael@0: ? ::DefWindowProcW(mProxyWindow, WM_SYSCOMMAND, wParam, lParam) michael@0: : ::SendMessageW(mWnd, WM_SYSCOMMAND, wParam, lParam); michael@0: return 0; michael@0: } michael@0: return TaskbarPreview::WndProc(nMsg, wParam, lParam); michael@0: } michael@0: michael@0: /* static */ michael@0: LRESULT CALLBACK michael@0: TaskbarTabPreview::GlobalWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam) { michael@0: TaskbarTabPreview *preview(nullptr); michael@0: if (nMsg == WM_CREATE) { michael@0: CREATESTRUCT *cs = reinterpret_cast(lParam); michael@0: preview = reinterpret_cast(cs->lpCreateParams); michael@0: if (!::SetPropW(hWnd, TASKBARPREVIEW_HWNDID, preview)) michael@0: NS_ERROR("Could not associate native window with tab preview"); michael@0: preview->mProxyWindow = hWnd; michael@0: } else { michael@0: preview = reinterpret_cast(::GetPropW(hWnd, TASKBARPREVIEW_HWNDID)); michael@0: if (nMsg == WM_DESTROY) michael@0: ::RemovePropW(hWnd, TASKBARPREVIEW_HWNDID); michael@0: } michael@0: michael@0: if (preview) michael@0: return preview->WndProc(nMsg, wParam, lParam); michael@0: return ::DefWindowProcW(hWnd, nMsg, wParam, lParam); michael@0: } michael@0: michael@0: nsresult michael@0: TaskbarTabPreview::Enable() { michael@0: WNDCLASSW wc; michael@0: HINSTANCE module = GetModuleHandle(nullptr); michael@0: michael@0: if (!GetClassInfoW(module, kWindowClass, &wc)) { michael@0: wc.style = 0; michael@0: wc.lpfnWndProc = GlobalWndProc; michael@0: wc.cbClsExtra = 0; michael@0: wc.cbWndExtra = 0; michael@0: wc.hInstance = module; michael@0: wc.hIcon = nullptr; michael@0: wc.hCursor = nullptr; michael@0: wc.hbrBackground = (HBRUSH) nullptr; michael@0: wc.lpszMenuName = (LPCWSTR) nullptr; michael@0: wc.lpszClassName = kWindowClass; michael@0: RegisterClassW(&wc); michael@0: } michael@0: ::CreateWindowW(kWindowClass, L"TaskbarPreviewWindow", michael@0: WS_CAPTION | WS_SYSMENU, 0, 0, 200, 60, michael@0: nullptr, nullptr, module, this); michael@0: // GlobalWndProc will set mProxyWindow so that WM_CREATE can have a valid HWND michael@0: if (!mProxyWindow) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: UpdateProxyWindowStyle(); michael@0: michael@0: nsresult rv = TaskbarPreview::Enable(); michael@0: nsresult rvUpdate; michael@0: rvUpdate = UpdateTitle(); michael@0: if (NS_FAILED(rvUpdate)) michael@0: rv = rvUpdate; michael@0: michael@0: rvUpdate = UpdateIcon(); michael@0: if (NS_FAILED(rvUpdate)) michael@0: rv = rvUpdate; michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: TaskbarTabPreview::Disable() { michael@0: // TaskbarPreview::Disable assumes that mWnd is valid but this method can be michael@0: // called when it is null iff the nsWindow has already been destroyed and we michael@0: // are still visible for some reason during object destruction. michael@0: if (mWnd) michael@0: TaskbarPreview::Disable(); michael@0: michael@0: if (FAILED(mTaskbar->UnregisterTab(mProxyWindow))) michael@0: return NS_ERROR_FAILURE; michael@0: mRegistered = false; michael@0: michael@0: // TaskbarPreview::WndProc will set mProxyWindow to null michael@0: if (!DestroyWindow(mProxyWindow)) michael@0: return NS_ERROR_FAILURE; michael@0: mProxyWindow = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: TaskbarTabPreview::DetachFromNSWindow() { michael@0: (void) SetVisible(false); michael@0: WindowHook &hook = GetWindowHook(); michael@0: hook.RemoveMonitor(WM_WINDOWPOSCHANGED, MainWindowHook, this); michael@0: michael@0: TaskbarPreview::DetachFromNSWindow(); michael@0: } michael@0: michael@0: /* static */ michael@0: bool michael@0: TaskbarTabPreview::MainWindowHook(void *aContext, michael@0: HWND hWnd, UINT nMsg, michael@0: WPARAM wParam, LPARAM lParam, michael@0: LRESULT *aResult) { michael@0: if (nMsg == WM_WINDOWPOSCHANGED) { michael@0: TaskbarTabPreview *preview = reinterpret_cast(aContext); michael@0: WINDOWPOS *pos = reinterpret_cast(lParam); michael@0: if (SWP_FRAMECHANGED == (pos->flags & SWP_FRAMECHANGED)) michael@0: preview->UpdateProxyWindowStyle(); michael@0: } else { michael@0: NS_NOTREACHED("Style changed hook fired on non-style changed message"); michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: TaskbarTabPreview::UpdateProxyWindowStyle() { michael@0: if (!mProxyWindow) michael@0: return; michael@0: michael@0: DWORD minMaxMask = WS_MINIMIZE | WS_MAXIMIZE; michael@0: DWORD windowStyle = GetWindowLongW(mWnd, GWL_STYLE); michael@0: michael@0: DWORD proxyStyle = GetWindowLongW(mProxyWindow, GWL_STYLE); michael@0: proxyStyle &= ~minMaxMask; michael@0: proxyStyle |= windowStyle & minMaxMask; michael@0: SetWindowLongW(mProxyWindow, GWL_STYLE, proxyStyle); michael@0: michael@0: DWORD exStyle = (WS_MAXIMIZE == (windowStyle & WS_MAXIMIZE)) ? WS_EX_TOOLWINDOW : 0; michael@0: SetWindowLongW(mProxyWindow, GWL_EXSTYLE, exStyle); michael@0: } michael@0: michael@0: nsresult michael@0: TaskbarTabPreview::UpdateTitle() { michael@0: NS_ASSERTION(mVisible, "UpdateTitle called on invisible preview"); michael@0: michael@0: if (!::SetWindowTextW(mProxyWindow, mTitle.get())) michael@0: return NS_ERROR_FAILURE; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: TaskbarTabPreview::UpdateIcon() { michael@0: NS_ASSERTION(mVisible, "UpdateIcon called on invisible preview"); michael@0: michael@0: ::SendMessageW(mProxyWindow, WM_SETICON, ICON_SMALL, (LPARAM)mIcon); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: TaskbarTabPreview::UpdateNext() { michael@0: NS_ASSERTION(CanMakeTaskbarCalls() && mVisible, "UpdateNext called on invisible tab preview"); michael@0: HWND hNext = nullptr; michael@0: if (mNext) { michael@0: bool visible; michael@0: nsresult rv = mNext->GetVisible(&visible); michael@0: michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Can only move next to enabled previews michael@0: if (!visible) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: hNext = (HWND)mNext->GetHWND(); michael@0: michael@0: // hNext must be registered with the taskbar if the call is to succeed michael@0: mNext->EnsureRegistration(); michael@0: } michael@0: if (FAILED(mTaskbar->SetTabOrder(mProxyWindow, hNext))) michael@0: return NS_ERROR_FAILURE; michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: } // namespace widget michael@0: } // namespace mozilla michael@0: