The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
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.

208 lines
7.5KB

  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. namespace MouseCursorHelpers
  18. {
  19. extern NSImage* createNSImage (const Image&);
  20. }
  21. class SystemTrayIconComponent::Pimpl
  22. {
  23. public:
  24. Pimpl (SystemTrayIconComponent& iconComp, const Image& im)
  25. : owner (iconComp), statusItem (nil),
  26. statusIcon (MouseCursorHelpers::createNSImage (im)),
  27. isHighlighted (false)
  28. {
  29. static SystemTrayViewClass cls;
  30. view = [cls.createInstance() init];
  31. SystemTrayViewClass::setOwner (view, this);
  32. SystemTrayViewClass::setImage (view, statusIcon);
  33. setIconSize();
  34. statusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength: NSSquareStatusItemLength] retain];
  35. [statusItem setView: view];
  36. }
  37. ~Pimpl()
  38. {
  39. [[NSStatusBar systemStatusBar] removeStatusItem: statusItem];
  40. [statusItem release];
  41. [view release];
  42. [statusIcon release];
  43. }
  44. void updateIcon (const Image& newImage)
  45. {
  46. [statusIcon release];
  47. statusIcon = MouseCursorHelpers::createNSImage (newImage);
  48. setIconSize();
  49. SystemTrayViewClass::setImage (view, statusIcon);
  50. }
  51. void setHighlighted (bool shouldHighlight)
  52. {
  53. isHighlighted = shouldHighlight;
  54. [view setNeedsDisplay: true];
  55. }
  56. void handleStatusItemAction (NSEvent* e)
  57. {
  58. NSEventType type = [e type];
  59. const bool isLeft = (type == NSLeftMouseDown || type == NSLeftMouseUp);
  60. const bool isRight = (type == NSRightMouseDown || type == NSRightMouseUp);
  61. if (owner.isCurrentlyBlockedByAnotherModalComponent())
  62. {
  63. if (isLeft || isRight)
  64. if (Component* const current = Component::getCurrentlyModalComponent())
  65. current->inputAttemptWhenModal();
  66. }
  67. else
  68. {
  69. ModifierKeys eventMods (ModifierKeys::getCurrentModifiersRealtime());
  70. if (([e modifierFlags] & NSCommandKeyMask) != 0)
  71. eventMods = eventMods.withFlags (ModifierKeys::commandModifier);
  72. NSRect r = [[e window] frame];
  73. r.origin.y = [[[NSScreen screens] objectAtIndex: 0] frame].size.height - r.origin.y - r.size.height;
  74. owner.setBounds (convertToRectInt (r));
  75. const Time now (Time::getCurrentTime());
  76. if (isLeft || isRight) // Only mouse up is sent by the OS, so simulate a down/up
  77. {
  78. owner.mouseDown (MouseEvent (Desktop::getInstance().getMainMouseSource(),
  79. Point<int>(),
  80. eventMods.withFlags (isLeft ? ModifierKeys::leftButtonModifier
  81. : ModifierKeys::rightButtonModifier),
  82. &owner, &owner, now,
  83. Point<int>(), now, 1, false));
  84. owner.mouseUp (MouseEvent (Desktop::getInstance().getMainMouseSource(),
  85. Point<int>(), eventMods.withoutMouseButtons(),
  86. &owner, &owner, now,
  87. Point<int>(), now, 1, false));
  88. }
  89. else if (type == NSMouseMoved)
  90. {
  91. owner.mouseMove (MouseEvent (Desktop::getInstance().getMainMouseSource(),
  92. Point<int>(), eventMods,
  93. &owner, &owner, now,
  94. Point<int>(), now, 1, false));
  95. }
  96. }
  97. }
  98. private:
  99. SystemTrayIconComponent& owner;
  100. NSStatusItem* statusItem;
  101. NSImage* statusIcon;
  102. NSControl* view;
  103. bool isHighlighted;
  104. void setIconSize()
  105. {
  106. [statusIcon setSize: NSMakeSize (20.0f, 20.0f)];
  107. }
  108. struct SystemTrayViewClass : public ObjCClass <NSControl>
  109. {
  110. SystemTrayViewClass() : ObjCClass <NSControl> ("JUCESystemTrayView_")
  111. {
  112. addIvar<Pimpl*> ("owner");
  113. addIvar<NSImage*> ("image");
  114. addMethod (@selector (mouseDown:), handleEventDown, "v@:@");
  115. addMethod (@selector (rightMouseDown:), handleEventDown, "v@:@");
  116. addMethod (@selector (drawRect:), drawRect, "v@:@");
  117. registerClass();
  118. }
  119. static Pimpl* getOwner (id self) { return getIvar<Pimpl*> (self, "owner"); }
  120. static NSImage* getImage (id self) { return getIvar<NSImage*> (self, "image"); }
  121. static void setOwner (id self, Pimpl* owner) { object_setInstanceVariable (self, "owner", owner); }
  122. static void setImage (id self, NSImage* image) { object_setInstanceVariable (self, "image", image); }
  123. private:
  124. static void handleEventDown (id self, SEL, NSEvent* e)
  125. {
  126. if (Pimpl* const owner = getOwner (self))
  127. {
  128. owner->setHighlighted (! owner->isHighlighted);
  129. owner->handleStatusItemAction (e);
  130. }
  131. }
  132. static void drawRect (id self, SEL, NSRect)
  133. {
  134. NSRect bounds = [self bounds];
  135. if (Pimpl* const owner = getOwner (self))
  136. [owner->statusItem drawStatusBarBackgroundInRect: bounds
  137. withHighlight: owner->isHighlighted];
  138. if (NSImage* const im = getImage (self))
  139. {
  140. NSSize imageSize = [im size];
  141. [im drawInRect: NSMakeRect (bounds.origin.x + ((bounds.size.width - imageSize.width) / 2.0f),
  142. bounds.origin.y + ((bounds.size.height - imageSize.height) / 2.0f),
  143. imageSize.width, imageSize.height)
  144. fromRect: NSZeroRect
  145. operation: NSCompositeSourceOver
  146. fraction: 1.0f];
  147. }
  148. }
  149. };
  150. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
  151. };
  152. //==============================================================================
  153. void SystemTrayIconComponent::setIconImage (const Image& newImage)
  154. {
  155. if (newImage.isValid())
  156. {
  157. if (pimpl == nullptr)
  158. pimpl = new Pimpl (*this, newImage);
  159. else
  160. pimpl->updateIcon (newImage);
  161. }
  162. else
  163. {
  164. pimpl = nullptr;
  165. }
  166. }
  167. void SystemTrayIconComponent::setIconTooltip (const String&)
  168. {
  169. // xxx not yet implemented!
  170. }