Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

236 lines
7.9KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. extern void* getUser32Function (const char*);
  18. namespace IconConverters
  19. {
  20. extern HICON createHICONFromImage (const Image&, BOOL isIcon, int hotspotX, int hotspotY);
  21. }
  22. //==============================================================================
  23. class SystemTrayIconComponent::Pimpl
  24. {
  25. public:
  26. Pimpl (SystemTrayIconComponent& owner_, HICON hicon, HWND hwnd)
  27. : owner (owner_),
  28. originalWndProc ((WNDPROC) GetWindowLongPtr (hwnd, GWLP_WNDPROC)),
  29. taskbarCreatedMessage (RegisterWindowMessage (TEXT ("TaskbarCreated")))
  30. {
  31. SetWindowLongPtr (hwnd, GWLP_WNDPROC, (LONG_PTR) hookedWndProc);
  32. zerostruct (iconData);
  33. iconData.cbSize = sizeof (iconData);
  34. iconData.hWnd = hwnd;
  35. iconData.uID = (UINT) (pointer_sized_int) hwnd;
  36. iconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
  37. iconData.uCallbackMessage = WM_TRAYNOTIFY;
  38. iconData.hIcon = hicon;
  39. notify (NIM_ADD);
  40. // In order to receive the "TaskbarCreated" message, we need to request that it's not filtered out.
  41. // (Need to load dynamically, as ChangeWindowMessageFilter is only available in Vista and later)
  42. typedef BOOL (WINAPI* ChangeWindowMessageFilterType) (UINT, DWORD);
  43. if (ChangeWindowMessageFilterType changeWindowMessageFilter
  44. = (ChangeWindowMessageFilterType) getUser32Function ("ChangeWindowMessageFilter"))
  45. changeWindowMessageFilter (taskbarCreatedMessage, 1 /* MSGFLT_ADD */);
  46. }
  47. ~Pimpl()
  48. {
  49. SetWindowLongPtr (iconData.hWnd, GWLP_WNDPROC, (LONG_PTR) originalWndProc);
  50. iconData.uFlags = 0;
  51. notify (NIM_DELETE);
  52. DestroyIcon (iconData.hIcon);
  53. }
  54. void updateIcon (HICON hicon)
  55. {
  56. HICON oldIcon = iconData.hIcon;
  57. iconData.hIcon = hicon;
  58. iconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
  59. notify (NIM_MODIFY);
  60. DestroyIcon (oldIcon);
  61. }
  62. void setToolTip (const String& toolTip)
  63. {
  64. iconData.uFlags = NIF_TIP;
  65. toolTip.copyToUTF16 (iconData.szTip, sizeof (iconData.szTip) - 1);
  66. notify (NIM_MODIFY);
  67. }
  68. void handleTaskBarEvent (const LPARAM lParam)
  69. {
  70. if (owner.isCurrentlyBlockedByAnotherModalComponent())
  71. {
  72. if (lParam == WM_LBUTTONDOWN || lParam == WM_RBUTTONDOWN
  73. || lParam == WM_LBUTTONDBLCLK || lParam == WM_LBUTTONDBLCLK)
  74. {
  75. if (Component* const current = Component::getCurrentlyModalComponent())
  76. current->inputAttemptWhenModal();
  77. }
  78. }
  79. else
  80. {
  81. ModifierKeys eventMods (ModifierKeys::getCurrentModifiersRealtime());
  82. if (lParam == WM_LBUTTONDOWN || lParam == WM_LBUTTONDBLCLK)
  83. eventMods = eventMods.withFlags (ModifierKeys::leftButtonModifier);
  84. else if (lParam == WM_RBUTTONDOWN || lParam == WM_RBUTTONDBLCLK)
  85. eventMods = eventMods.withFlags (ModifierKeys::rightButtonModifier);
  86. else if (lParam == WM_LBUTTONUP || lParam == WM_RBUTTONUP)
  87. eventMods = eventMods.withoutMouseButtons();
  88. const Time eventTime (getMouseEventTime());
  89. const MouseEvent e (Desktop::getInstance().getMainMouseSource(),
  90. Point<float>(), eventMods, &owner, &owner, eventTime,
  91. Point<float>(), eventTime, 1, false);
  92. if (lParam == WM_LBUTTONDOWN || lParam == WM_RBUTTONDOWN)
  93. {
  94. SetFocus (iconData.hWnd);
  95. SetForegroundWindow (iconData.hWnd);
  96. owner.mouseDown (e);
  97. }
  98. else if (lParam == WM_LBUTTONUP || lParam == WM_RBUTTONUP)
  99. {
  100. owner.mouseUp (e);
  101. }
  102. else if (lParam == WM_LBUTTONDBLCLK || lParam == WM_RBUTTONDBLCLK)
  103. {
  104. owner.mouseDoubleClick (e);
  105. }
  106. else if (lParam == WM_MOUSEMOVE)
  107. {
  108. owner.mouseMove (e);
  109. }
  110. }
  111. }
  112. static Pimpl* getPimpl (HWND hwnd)
  113. {
  114. if (JuceWindowIdentifier::isJUCEWindow (hwnd))
  115. if (ComponentPeer* peer = (ComponentPeer*) GetWindowLongPtr (hwnd, 8))
  116. if (SystemTrayIconComponent* const iconComp = dynamic_cast<SystemTrayIconComponent*> (&(peer->getComponent())))
  117. return iconComp->pimpl;
  118. return nullptr;
  119. }
  120. static LRESULT CALLBACK hookedWndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  121. {
  122. if (Pimpl* const p = getPimpl (hwnd))
  123. return p->windowProc (hwnd, message, wParam, lParam);
  124. return DefWindowProcW (hwnd, message, wParam, lParam);
  125. }
  126. LRESULT windowProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  127. {
  128. if (message == WM_TRAYNOTIFY)
  129. {
  130. handleTaskBarEvent (lParam);
  131. }
  132. else if (message == taskbarCreatedMessage)
  133. {
  134. iconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
  135. notify (NIM_ADD);
  136. }
  137. return CallWindowProc (originalWndProc, hwnd, message, wParam, lParam);
  138. }
  139. void showBubble (const String& title, const String& content)
  140. {
  141. iconData.uFlags = 0x10 /*NIF_INFO*/;
  142. title.copyToUTF16 (iconData.szInfoTitle, sizeof (iconData.szInfoTitle) - 1);
  143. content.copyToUTF16 (iconData.szInfo, sizeof (iconData.szInfo) - 1);
  144. notify (NIM_MODIFY);
  145. }
  146. SystemTrayIconComponent& owner;
  147. NOTIFYICONDATA iconData;
  148. private:
  149. WNDPROC originalWndProc;
  150. const DWORD taskbarCreatedMessage;
  151. enum { WM_TRAYNOTIFY = WM_USER + 100 };
  152. void notify (DWORD message) noexcept { Shell_NotifyIcon (message, &iconData); }
  153. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
  154. };
  155. //==============================================================================
  156. void SystemTrayIconComponent::setIconImage (const Image& newImage)
  157. {
  158. if (newImage.isValid())
  159. {
  160. HICON hicon = IconConverters::createHICONFromImage (newImage, TRUE, 0, 0);
  161. if (pimpl == nullptr)
  162. pimpl = new Pimpl (*this, hicon, (HWND) getWindowHandle());
  163. else
  164. pimpl->updateIcon (hicon);
  165. }
  166. else
  167. {
  168. pimpl = nullptr;
  169. }
  170. }
  171. void SystemTrayIconComponent::setIconTooltip (const String& tooltip)
  172. {
  173. if (pimpl != nullptr)
  174. pimpl->setToolTip (tooltip);
  175. }
  176. void SystemTrayIconComponent::setHighlighted (bool)
  177. {
  178. // N/A on Windows.
  179. }
  180. void SystemTrayIconComponent::showInfoBubble (const String& title, const String& content)
  181. {
  182. if (pimpl != nullptr)
  183. pimpl->showBubble (title, content);
  184. }
  185. void SystemTrayIconComponent::hideInfoBubble()
  186. {
  187. showInfoBubble (String::empty, String::empty);
  188. }
  189. void* SystemTrayIconComponent::getNativeHandle() const
  190. {
  191. return pimpl != nullptr ? &(pimpl->iconData) : nullptr;
  192. }