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.

244 lines
8.1KB

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