/* ============================================================================== This file is part of the JUCE library - "Jules' Utility Class Extensions" Copyright 2004-11 by Raw Material Software Ltd. ------------------------------------------------------------------------------ JUCE can be redistributed and/or modified under the terms of the GNU General Public License (Version 2), as published by the Free Software Foundation. A copy of the license is included in the JUCE distribution, or can be found online at www.gnu.org/licenses. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.rawmaterialsoftware.com/juce for more information. ============================================================================== */ namespace IconConverters { extern HICON createHICONFromImage (const Image& image, const BOOL isIcon, int hotspotX, int hotspotY); } //============================================================================== class SystemTrayIconComponent::Pimpl { public: Pimpl (SystemTrayIconComponent& owner_, HICON hicon, HWND hwnd) : owner (owner_), originalWndProc ((WNDPROC) GetWindowLongPtr (hwnd, GWLP_WNDPROC)), taskbarCreatedMessage (RegisterWindowMessage (TEXT ("TaskbarCreated"))) { SetWindowLongPtr (hwnd, GWLP_WNDPROC, (LONG_PTR) hookedWndProc); zerostruct (iconData); iconData.cbSize = sizeof (iconData); iconData.hWnd = hwnd; iconData.uID = (UINT) (pointer_sized_int) hwnd; iconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; iconData.uCallbackMessage = WM_TRAYNOTIFY; iconData.hIcon = hicon; Shell_NotifyIcon (NIM_ADD, &iconData); // In order to receive the "TaskbarCreated" message, we need to request that it's not filtered out. // (Need to load dynamically, as ChangeWindowMessageFilter is only available in Vista and later) typedef BOOL (WINAPI* ChangeWindowMessageFilterType) (UINT, DWORD); ChangeWindowMessageFilterType changeWindowMessageFilter = (ChangeWindowMessageFilterType) getUser32Function ("ChangeWindowMessageFilter"); if (changeWindowMessageFilter != nullptr) changeWindowMessageFilter (taskbarCreatedMessage, 1 /* MSGFLT_ADD */); } ~Pimpl() { SetWindowLongPtr (iconData.hWnd, GWLP_WNDPROC, (LONG_PTR) originalWndProc); iconData.uFlags = 0; Shell_NotifyIcon (NIM_DELETE, &iconData); DestroyIcon (iconData.hIcon); } void updateIcon (HICON hicon) { HICON oldIcon = iconData.hIcon; iconData.hIcon = hicon; iconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; Shell_NotifyIcon (NIM_MODIFY, &iconData); DestroyIcon (oldIcon); } void setToolTip (const String& toolTip) { iconData.uFlags = NIF_TIP; toolTip.copyToUTF16 (iconData.szTip, sizeof (iconData.szTip) - 1); Shell_NotifyIcon (NIM_MODIFY, &iconData); } void handleTaskBarEvent (const LPARAM lParam) { if (owner.isCurrentlyBlockedByAnotherModalComponent()) { if (lParam == WM_LBUTTONDOWN || lParam == WM_RBUTTONDOWN || lParam == WM_LBUTTONDBLCLK || lParam == WM_LBUTTONDBLCLK) { Component* const current = Component::getCurrentlyModalComponent(); if (current != nullptr) current->inputAttemptWhenModal(); } } else { ModifierKeys eventMods (ModifierKeys::getCurrentModifiersRealtime()); if (lParam == WM_LBUTTONDOWN || lParam == WM_LBUTTONDBLCLK) eventMods = eventMods.withFlags (ModifierKeys::leftButtonModifier); else if (lParam == WM_RBUTTONDOWN || lParam == WM_RBUTTONDBLCLK) eventMods = eventMods.withFlags (ModifierKeys::rightButtonModifier); else if (lParam == WM_LBUTTONUP || lParam == WM_RBUTTONUP) eventMods = eventMods.withoutMouseButtons(); const MouseEvent e (Desktop::getInstance().getMainMouseSource(), Point(), eventMods, &owner, &owner, Time (getMouseEventTime()), Point(), Time (getMouseEventTime()), 1, false); if (lParam == WM_LBUTTONDOWN || lParam == WM_RBUTTONDOWN) { SetFocus (iconData.hWnd); SetForegroundWindow (iconData.hWnd); owner.mouseDown (e); } else if (lParam == WM_LBUTTONUP || lParam == WM_RBUTTONUP) { owner.mouseUp (e); } else if (lParam == WM_LBUTTONDBLCLK || lParam == WM_LBUTTONDBLCLK) { owner.mouseDoubleClick (e); } else if (lParam == WM_MOUSEMOVE) { owner.mouseMove (e); } } } static Pimpl* getPimpl (HWND hwnd) { if (JuceWindowIdentifier::isJUCEWindow (hwnd)) { ComponentPeer* peer = (ComponentPeer*) GetWindowLongPtr (hwnd, 8); if (peer != nullptr) { SystemTrayIconComponent* const iconComp = dynamic_cast (peer->getComponent()); if (iconComp != nullptr) return iconComp->pimpl; } } return nullptr; } static LRESULT CALLBACK hookedWndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { Pimpl* const p = getPimpl (hwnd); if (p != nullptr) return p->windowProc (hwnd, message, wParam, lParam); else return DefWindowProcW (hwnd, message, wParam, lParam); } LRESULT windowProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { if (message == WM_TRAYNOTIFY) { handleTaskBarEvent (lParam); } else if (message == taskbarCreatedMessage) { iconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; Shell_NotifyIcon (NIM_ADD, &iconData); } return CallWindowProc (originalWndProc, hwnd, message, wParam, lParam); } private: SystemTrayIconComponent& owner; NOTIFYICONDATA iconData; WNDPROC originalWndProc; const DWORD taskbarCreatedMessage; enum { WM_TRAYNOTIFY = WM_USER + 100 }; static void* getUser32Function (const char* functionName) { HMODULE user32Mod = GetModuleHandle (_T("user32.dll")); jassert (user32Mod != 0); return GetProcAddress (user32Mod, functionName); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl); }; //============================================================================== void SystemTrayIconComponent::setIconImage (const Image& newImage) { if (newImage.isValid()) { HICON hicon = IconConverters::createHICONFromImage (newImage, TRUE, 0, 0); if (pimpl == nullptr) pimpl = new Pimpl (*this, hicon, (HWND) getWindowHandle()); else pimpl->updateIcon (hicon); } else { pimpl = nullptr; } } void SystemTrayIconComponent::setIconTooltip (const String& tooltip) { if (pimpl != nullptr) pimpl->setToolTip (tooltip); }