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.

778 lines
28KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE 7 technical preview.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. You may use this code under the terms of the GPL v3
  6. (see www.gnu.org/licenses).
  7. For the technical preview this file cannot be licensed commercially.
  8. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  9. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  10. DISCLAIMED.
  11. ==============================================================================
  12. */
  13. namespace juce
  14. {
  15. void LookAndFeel::playAlertSound()
  16. {
  17. NSBeep();
  18. }
  19. //==============================================================================
  20. class OSXMessageBox : private AsyncUpdater
  21. {
  22. public:
  23. OSXMessageBox (const MessageBoxOptions& opts,
  24. std::unique_ptr<ModalComponentManager::Callback>&& c)
  25. : options (opts), callback (std::move (c))
  26. {
  27. }
  28. int getResult() const
  29. {
  30. return convertResult ([getAlert() runModal]);
  31. }
  32. using AsyncUpdater::triggerAsyncUpdate;
  33. private:
  34. static int convertResult (NSModalResponse response)
  35. {
  36. switch (response)
  37. {
  38. case NSAlertFirstButtonReturn: return 0;
  39. case NSAlertSecondButtonReturn: return 1;
  40. case NSAlertThirdButtonReturn: return 2;
  41. default: break;
  42. }
  43. jassertfalse;
  44. return 0;
  45. }
  46. void handleAsyncUpdate() override
  47. {
  48. if (auto* comp = options.getAssociatedComponent())
  49. {
  50. if (auto* peer = comp->getPeer())
  51. {
  52. if (auto* view = static_cast<NSView*> (peer->getNativeHandle()))
  53. {
  54. if (auto* window = [view window])
  55. {
  56. if (@available (macOS 10.9, *))
  57. {
  58. [getAlert() beginSheetModalForWindow: window completionHandler: ^(NSModalResponse result)
  59. {
  60. handleModalFinished (result);
  61. }];
  62. return;
  63. }
  64. }
  65. }
  66. }
  67. }
  68. handleModalFinished ([getAlert() runModal]);
  69. }
  70. void handleModalFinished (NSModalResponse result)
  71. {
  72. if (callback != nullptr)
  73. callback->modalStateFinished (convertResult (result));
  74. delete this;
  75. }
  76. static void addButton (NSAlert* alert, const String& button)
  77. {
  78. if (! button.isEmpty())
  79. [alert addButtonWithTitle: juceStringToNS (button)];
  80. }
  81. NSAlert* getAlert() const
  82. {
  83. NSAlert* alert = [[[NSAlert alloc] init] autorelease];
  84. [alert setMessageText: juceStringToNS (options.getTitle())];
  85. [alert setInformativeText: juceStringToNS (options.getMessage())];
  86. [alert setAlertStyle: options.getIconType() == MessageBoxIconType::WarningIcon ? NSAlertStyleCritical
  87. : NSAlertStyleInformational];
  88. const auto button1Text = options.getButtonText (0);
  89. addButton (alert, button1Text.isEmpty() ? "OK" : button1Text);
  90. addButton (alert, options.getButtonText (1));
  91. addButton (alert, options.getButtonText (2));
  92. return alert;
  93. }
  94. MessageBoxOptions options;
  95. std::unique_ptr<ModalComponentManager::Callback> callback;
  96. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSXMessageBox)
  97. };
  98. static int showDialog (const MessageBoxOptions& options,
  99. ModalComponentManager::Callback* callbackIn,
  100. AlertWindowMappings::MapFn mapFn)
  101. {
  102. #if JUCE_MODAL_LOOPS_PERMITTED
  103. if (callbackIn == nullptr)
  104. {
  105. jassert (mapFn != nullptr);
  106. OSXMessageBox messageBox (options, nullptr);
  107. return mapFn (messageBox.getResult());
  108. }
  109. #endif
  110. auto messageBox = std::make_unique<OSXMessageBox> (options,
  111. AlertWindowMappings::getWrappedCallback (callbackIn, mapFn));
  112. messageBox->triggerAsyncUpdate();
  113. messageBox.release();
  114. return 0;
  115. }
  116. #if JUCE_MODAL_LOOPS_PERMITTED
  117. void JUCE_CALLTYPE NativeMessageBox::showMessageBox (MessageBoxIconType iconType,
  118. const String& title, const String& message,
  119. Component* associatedComponent)
  120. {
  121. showDialog (MessageBoxOptions()
  122. .withIconType (iconType)
  123. .withTitle (title)
  124. .withMessage (message)
  125. .withButton (TRANS("OK"))
  126. .withAssociatedComponent (associatedComponent),
  127. nullptr, AlertWindowMappings::messageBox);
  128. }
  129. int JUCE_CALLTYPE NativeMessageBox::show (const MessageBoxOptions& options)
  130. {
  131. return showDialog (options, nullptr, AlertWindowMappings::noMapping);
  132. }
  133. #endif
  134. void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (MessageBoxIconType iconType,
  135. const String& title, const String& message,
  136. Component* associatedComponent,
  137. ModalComponentManager::Callback* callback)
  138. {
  139. showDialog (MessageBoxOptions()
  140. .withIconType (iconType)
  141. .withTitle (title)
  142. .withMessage (message)
  143. .withButton (TRANS("OK"))
  144. .withAssociatedComponent (associatedComponent),
  145. callback, AlertWindowMappings::messageBox);
  146. }
  147. bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (MessageBoxIconType iconType,
  148. const String& title, const String& message,
  149. Component* associatedComponent,
  150. ModalComponentManager::Callback* callback)
  151. {
  152. return showDialog (MessageBoxOptions()
  153. .withIconType (iconType)
  154. .withTitle (title)
  155. .withMessage (message)
  156. .withButton (TRANS("OK"))
  157. .withButton (TRANS("Cancel"))
  158. .withAssociatedComponent (associatedComponent),
  159. callback, AlertWindowMappings::okCancel) != 0;
  160. }
  161. int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (MessageBoxIconType iconType,
  162. const String& title, const String& message,
  163. Component* associatedComponent,
  164. ModalComponentManager::Callback* callback)
  165. {
  166. return showDialog (MessageBoxOptions()
  167. .withIconType (iconType)
  168. .withTitle (title)
  169. .withMessage (message)
  170. .withButton (TRANS("Yes"))
  171. .withButton (TRANS("No"))
  172. .withButton (TRANS("Cancel"))
  173. .withAssociatedComponent (associatedComponent),
  174. callback, AlertWindowMappings::yesNoCancel);
  175. }
  176. int JUCE_CALLTYPE NativeMessageBox::showYesNoBox (MessageBoxIconType iconType,
  177. const String& title, const String& message,
  178. Component* associatedComponent,
  179. ModalComponentManager::Callback* callback)
  180. {
  181. return showDialog (MessageBoxOptions()
  182. .withIconType (iconType)
  183. .withTitle (title)
  184. .withMessage (message)
  185. .withButton (TRANS("Yes"))
  186. .withButton (TRANS("No"))
  187. .withAssociatedComponent (associatedComponent),
  188. callback, AlertWindowMappings::okCancel);
  189. }
  190. void JUCE_CALLTYPE NativeMessageBox::showAsync (const MessageBoxOptions& options,
  191. ModalComponentManager::Callback* callback)
  192. {
  193. showDialog (options, callback, AlertWindowMappings::noMapping);
  194. }
  195. void JUCE_CALLTYPE NativeMessageBox::showAsync (const MessageBoxOptions& options,
  196. std::function<void (int)> callback)
  197. {
  198. showAsync (options, ModalCallbackFunction::create (callback));
  199. }
  200. //==============================================================================
  201. static NSRect getDragRect (NSView* view, NSEvent* event)
  202. {
  203. auto eventPos = [event locationInWindow];
  204. return [view convertRect: NSMakeRect (eventPos.x - 16.0f, eventPos.y - 16.0f, 32.0f, 32.0f)
  205. fromView: nil];
  206. }
  207. static NSView* getNSViewForDragEvent (Component* sourceComp)
  208. {
  209. if (sourceComp == nullptr)
  210. if (auto* draggingSource = Desktop::getInstance().getDraggingMouseSource (0))
  211. sourceComp = draggingSource->getComponentUnderMouse();
  212. if (sourceComp != nullptr)
  213. return (NSView*) sourceComp->getWindowHandle();
  214. jassertfalse; // This method must be called in response to a component's mouseDown or mouseDrag event!
  215. return nil;
  216. }
  217. struct NSDraggingSourceHelper : public ObjCClass<NSObject<NSDraggingSource>>
  218. {
  219. NSDraggingSourceHelper() : ObjCClass<NSObject<NSDraggingSource>> ("JUCENSDraggingSourceHelper_")
  220. {
  221. addIvar<std::function<void()>*> ("callback");
  222. addIvar<String*> ("text");
  223. addIvar<NSDragOperation*> ("operation");
  224. addMethod (@selector (dealloc), dealloc);
  225. addMethod (@selector (pasteboard:item:provideDataForType:), provideDataForType);
  226. addMethod (@selector (draggingSession:sourceOperationMaskForDraggingContext:), sourceOperationMaskForDraggingContext);
  227. addMethod (@selector (draggingSession:endedAtPoint:operation:), draggingSessionEnded);
  228. addProtocol (@protocol (NSPasteboardItemDataProvider));
  229. registerClass();
  230. }
  231. static void setText (id self, const String& text)
  232. {
  233. object_setInstanceVariable (self, "text", new String (text));
  234. }
  235. static void setCompletionCallback (id self, std::function<void()> cb)
  236. {
  237. object_setInstanceVariable (self, "callback", new std::function<void()> (cb));
  238. }
  239. static void setDragOperation (id self, NSDragOperation op)
  240. {
  241. object_setInstanceVariable (self, "operation", new NSDragOperation (op));
  242. }
  243. private:
  244. static void dealloc (id self, SEL)
  245. {
  246. delete getIvar<String*> (self, "text");
  247. delete getIvar<std::function<void()>*> (self, "callback");
  248. delete getIvar<NSDragOperation*> (self, "operation");
  249. sendSuperclassMessage<void> (self, @selector (dealloc));
  250. }
  251. static void provideDataForType (id self, SEL, NSPasteboard* sender, NSPasteboardItem*, NSString* type)
  252. {
  253. if ([type compare: NSPasteboardTypeString] == NSOrderedSame)
  254. if (auto* text = getIvar<String*> (self, "text"))
  255. [sender setData: [juceStringToNS (*text) dataUsingEncoding: NSUTF8StringEncoding]
  256. forType: NSPasteboardTypeString];
  257. }
  258. static NSDragOperation sourceOperationMaskForDraggingContext (id self, SEL, NSDraggingSession*, NSDraggingContext)
  259. {
  260. return *getIvar<NSDragOperation*> (self, "operation");
  261. }
  262. static void draggingSessionEnded (id self, SEL, NSDraggingSession*, NSPoint p, NSDragOperation)
  263. {
  264. // Our view doesn't receive a mouse up when the drag ends so we need to generate one here and send it...
  265. if (auto* view = getNSViewForDragEvent (nullptr))
  266. if (auto* cgEvent = CGEventCreateMouseEvent (nullptr, kCGEventLeftMouseUp, CGPointMake (p.x, p.y), kCGMouseButtonLeft))
  267. if (id e = [NSEvent eventWithCGEvent: cgEvent])
  268. [view mouseUp: e];
  269. if (auto* cb = getIvar<std::function<void()>*> (self, "callback"))
  270. cb->operator()();
  271. }
  272. };
  273. static NSDraggingSourceHelper draggingSourceHelper;
  274. bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Component* sourceComponent,
  275. std::function<void()> callback)
  276. {
  277. if (text.isEmpty())
  278. return false;
  279. if (auto* view = getNSViewForDragEvent (sourceComponent))
  280. {
  281. JUCE_AUTORELEASEPOOL
  282. {
  283. if (auto event = [[view window] currentEvent])
  284. {
  285. id helper = [draggingSourceHelper.createInstance() init];
  286. NSDraggingSourceHelper::setText (helper, text);
  287. NSDraggingSourceHelper::setDragOperation (helper, NSDragOperationCopy);
  288. if (callback != nullptr)
  289. NSDraggingSourceHelper::setCompletionCallback (helper, callback);
  290. auto pasteboardItem = [[NSPasteboardItem new] autorelease];
  291. [pasteboardItem setDataProvider: helper
  292. forTypes: [NSArray arrayWithObjects: NSPasteboardTypeString, nil]];
  293. auto dragItem = [[[NSDraggingItem alloc] initWithPasteboardWriter: pasteboardItem] autorelease];
  294. NSImage* image = [[NSWorkspace sharedWorkspace] iconForFile: nsEmptyString()];
  295. [dragItem setDraggingFrame: getDragRect (view, event) contents: image];
  296. if (auto session = [view beginDraggingSessionWithItems: [NSArray arrayWithObject: dragItem]
  297. event: event
  298. source: helper])
  299. {
  300. session.animatesToStartingPositionsOnCancelOrFail = YES;
  301. session.draggingFormation = NSDraggingFormationNone;
  302. return true;
  303. }
  304. }
  305. }
  306. }
  307. return false;
  308. }
  309. bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, bool canMoveFiles,
  310. Component* sourceComponent, std::function<void()> callback)
  311. {
  312. if (files.isEmpty())
  313. return false;
  314. if (auto* view = getNSViewForDragEvent (sourceComponent))
  315. {
  316. JUCE_AUTORELEASEPOOL
  317. {
  318. if (auto event = [[view window] currentEvent])
  319. {
  320. auto dragItems = [[[NSMutableArray alloc] init] autorelease];
  321. for (auto& filename : files)
  322. {
  323. auto* nsFilename = juceStringToNS (filename);
  324. auto fileURL = [NSURL fileURLWithPath: nsFilename];
  325. auto dragItem = [[NSDraggingItem alloc] initWithPasteboardWriter: fileURL];
  326. auto eventPos = [event locationInWindow];
  327. auto dragRect = [view convertRect: NSMakeRect (eventPos.x - 16.0f, eventPos.y - 16.0f, 32.0f, 32.0f)
  328. fromView: nil];
  329. auto dragImage = [[NSWorkspace sharedWorkspace] iconForFile: nsFilename];
  330. [dragItem setDraggingFrame: dragRect
  331. contents: dragImage];
  332. [dragItems addObject: dragItem];
  333. [dragItem release];
  334. }
  335. auto helper = [draggingSourceHelper.createInstance() autorelease];
  336. if (callback != nullptr)
  337. NSDraggingSourceHelper::setCompletionCallback (helper, callback);
  338. NSDraggingSourceHelper::setDragOperation (helper, canMoveFiles ? NSDragOperationMove
  339. : NSDragOperationCopy);
  340. return [view beginDraggingSessionWithItems: dragItems
  341. event: event
  342. source: helper] != nullptr;
  343. }
  344. }
  345. }
  346. return false;
  347. }
  348. //==============================================================================
  349. bool Desktop::canUseSemiTransparentWindows() noexcept
  350. {
  351. return true;
  352. }
  353. Point<float> MouseInputSource::getCurrentRawMousePosition()
  354. {
  355. JUCE_AUTORELEASEPOOL
  356. {
  357. auto p = [NSEvent mouseLocation];
  358. return { (float) p.x, (float) (getMainScreenHeight() - p.y) };
  359. }
  360. }
  361. void MouseInputSource::setRawMousePosition (Point<float> newPosition)
  362. {
  363. // this rubbish needs to be done around the warp call, to avoid causing a
  364. // bizarre glitch..
  365. CGAssociateMouseAndMouseCursorPosition (false);
  366. CGWarpMouseCursorPosition (convertToCGPoint (newPosition));
  367. CGAssociateMouseAndMouseCursorPosition (true);
  368. }
  369. double Desktop::getDefaultMasterScale()
  370. {
  371. return 1.0;
  372. }
  373. Desktop::DisplayOrientation Desktop::getCurrentOrientation() const
  374. {
  375. return upright;
  376. }
  377. bool Desktop::isDarkModeActive() const
  378. {
  379. return [[[NSUserDefaults standardUserDefaults] stringForKey: nsStringLiteral ("AppleInterfaceStyle")]
  380. isEqualToString: nsStringLiteral ("Dark")];
  381. }
  382. class Desktop::NativeDarkModeChangeDetectorImpl
  383. {
  384. public:
  385. NativeDarkModeChangeDetectorImpl()
  386. {
  387. static DelegateClass delegateClass;
  388. delegate = [delegateClass.createInstance() init];
  389. object_setInstanceVariable (delegate, "owner", this);
  390. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
  391. [[NSDistributedNotificationCenter defaultCenter] addObserver: delegate
  392. selector: @selector (darkModeChanged:)
  393. name: @"AppleInterfaceThemeChangedNotification"
  394. object: nil];
  395. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  396. }
  397. ~NativeDarkModeChangeDetectorImpl()
  398. {
  399. object_setInstanceVariable (delegate, "owner", nullptr);
  400. [[NSDistributedNotificationCenter defaultCenter] removeObserver: delegate];
  401. [delegate release];
  402. }
  403. void darkModeChanged()
  404. {
  405. Desktop::getInstance().darkModeChanged();
  406. }
  407. private:
  408. struct DelegateClass : public ObjCClass<NSObject>
  409. {
  410. DelegateClass() : ObjCClass<NSObject> ("JUCEDelegate_")
  411. {
  412. addIvar<NativeDarkModeChangeDetectorImpl*> ("owner");
  413. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
  414. addMethod (@selector (darkModeChanged:), darkModeChanged);
  415. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  416. registerClass();
  417. }
  418. static void darkModeChanged (id self, SEL, NSNotification*)
  419. {
  420. if (auto* owner = getIvar<NativeDarkModeChangeDetectorImpl*> (self, "owner"))
  421. owner->darkModeChanged();
  422. }
  423. };
  424. id delegate = nil;
  425. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeDarkModeChangeDetectorImpl)
  426. };
  427. std::unique_ptr<Desktop::NativeDarkModeChangeDetectorImpl> Desktop::createNativeDarkModeChangeDetectorImpl()
  428. {
  429. return std::make_unique<NativeDarkModeChangeDetectorImpl>();
  430. }
  431. //==============================================================================
  432. class ScreenSaverDefeater : public Timer
  433. {
  434. public:
  435. ScreenSaverDefeater()
  436. {
  437. startTimer (5000);
  438. timerCallback();
  439. }
  440. void timerCallback() override
  441. {
  442. if (Process::isForegroundProcess())
  443. {
  444. if (assertion == nullptr)
  445. assertion.reset (new PMAssertion());
  446. }
  447. else
  448. {
  449. assertion.reset();
  450. }
  451. }
  452. struct PMAssertion
  453. {
  454. PMAssertion() : assertionID (kIOPMNullAssertionID)
  455. {
  456. IOReturn res = IOPMAssertionCreateWithName (kIOPMAssertionTypePreventUserIdleDisplaySleep,
  457. kIOPMAssertionLevelOn,
  458. CFSTR ("JUCE Playback"),
  459. &assertionID);
  460. jassert (res == kIOReturnSuccess); ignoreUnused (res);
  461. }
  462. ~PMAssertion()
  463. {
  464. if (assertionID != kIOPMNullAssertionID)
  465. IOPMAssertionRelease (assertionID);
  466. }
  467. IOPMAssertionID assertionID;
  468. };
  469. std::unique_ptr<PMAssertion> assertion;
  470. };
  471. static std::unique_ptr<ScreenSaverDefeater> screenSaverDefeater;
  472. void Desktop::setScreenSaverEnabled (const bool isEnabled)
  473. {
  474. if (isEnabled)
  475. screenSaverDefeater.reset();
  476. else if (screenSaverDefeater == nullptr)
  477. screenSaverDefeater.reset (new ScreenSaverDefeater());
  478. }
  479. bool Desktop::isScreenSaverEnabled()
  480. {
  481. return screenSaverDefeater == nullptr;
  482. }
  483. //==============================================================================
  484. struct DisplaySettingsChangeCallback : private DeletedAtShutdown
  485. {
  486. DisplaySettingsChangeCallback()
  487. {
  488. CGDisplayRegisterReconfigurationCallback (displayReconfigurationCallback, this);
  489. }
  490. ~DisplaySettingsChangeCallback()
  491. {
  492. CGDisplayRemoveReconfigurationCallback (displayReconfigurationCallback, this);
  493. clearSingletonInstance();
  494. }
  495. static void displayReconfigurationCallback (CGDirectDisplayID, CGDisplayChangeSummaryFlags, void* userInfo)
  496. {
  497. if (auto* thisPtr = static_cast<DisplaySettingsChangeCallback*> (userInfo))
  498. if (thisPtr->forceDisplayUpdate != nullptr)
  499. thisPtr->forceDisplayUpdate();
  500. }
  501. std::function<void()> forceDisplayUpdate;
  502. JUCE_DECLARE_SINGLETON (DisplaySettingsChangeCallback, false)
  503. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DisplaySettingsChangeCallback)
  504. };
  505. JUCE_IMPLEMENT_SINGLETON (DisplaySettingsChangeCallback)
  506. static Rectangle<int> convertDisplayRect (NSRect r, CGFloat mainScreenBottom)
  507. {
  508. r.origin.y = mainScreenBottom - (r.origin.y + r.size.height);
  509. return convertToRectInt (r);
  510. }
  511. static Displays::Display getDisplayFromScreen (NSScreen* s, CGFloat& mainScreenBottom, const float masterScale)
  512. {
  513. Displays::Display d;
  514. d.isMain = (mainScreenBottom == 0);
  515. if (d.isMain)
  516. mainScreenBottom = [s frame].size.height;
  517. d.userArea = convertDisplayRect ([s visibleFrame], mainScreenBottom) / masterScale;
  518. d.totalArea = convertDisplayRect ([s frame], mainScreenBottom) / masterScale;
  519. d.scale = masterScale;
  520. if ([s respondsToSelector: @selector (backingScaleFactor)])
  521. d.scale *= s.backingScaleFactor;
  522. NSSize dpi = [[[s deviceDescription] objectForKey: NSDeviceResolution] sizeValue];
  523. d.dpi = (dpi.width + dpi.height) / 2.0;
  524. return d;
  525. }
  526. void Displays::findDisplays (const float masterScale)
  527. {
  528. JUCE_AUTORELEASEPOOL
  529. {
  530. if (DisplaySettingsChangeCallback::getInstanceWithoutCreating() == nullptr)
  531. DisplaySettingsChangeCallback::getInstance()->forceDisplayUpdate = [this] { refresh(); };
  532. CGFloat mainScreenBottom = 0;
  533. for (NSScreen* s in [NSScreen screens])
  534. displays.add (getDisplayFromScreen (s, mainScreenBottom, masterScale));
  535. }
  536. }
  537. //==============================================================================
  538. bool juce_areThereAnyAlwaysOnTopWindows()
  539. {
  540. for (NSWindow* window in [NSApp windows])
  541. if ([window level] > NSNormalWindowLevel)
  542. return true;
  543. return false;
  544. }
  545. //==============================================================================
  546. static void selectImageForDrawing (const Image& image)
  547. {
  548. [NSGraphicsContext saveGraphicsState];
  549. if (@available (macOS 10.10, *))
  550. {
  551. [NSGraphicsContext setCurrentContext: [NSGraphicsContext graphicsContextWithCGContext: juce_getImageContext (image)
  552. flipped: false]];
  553. return;
  554. }
  555. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
  556. [NSGraphicsContext setCurrentContext: [NSGraphicsContext graphicsContextWithGraphicsPort: juce_getImageContext (image)
  557. flipped: false]];
  558. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  559. }
  560. static void releaseImageAfterDrawing()
  561. {
  562. [[NSGraphicsContext currentContext] flushGraphics];
  563. [NSGraphicsContext restoreGraphicsState];
  564. }
  565. Image juce_createIconForFile (const File& file)
  566. {
  567. JUCE_AUTORELEASEPOOL
  568. {
  569. NSImage* image = [[NSWorkspace sharedWorkspace] iconForFile: juceStringToNS (file.getFullPathName())];
  570. Image result (Image::ARGB, (int) [image size].width, (int) [image size].height, true);
  571. selectImageForDrawing (result);
  572. [image drawAtPoint: NSMakePoint (0, 0)
  573. fromRect: NSMakeRect (0, 0, [image size].width, [image size].height)
  574. operation: NSCompositingOperationSourceOver fraction: 1.0f];
  575. releaseImageAfterDrawing();
  576. return result;
  577. }
  578. }
  579. static Image createNSWindowSnapshot (NSWindow* nsWindow)
  580. {
  581. JUCE_AUTORELEASEPOOL
  582. {
  583. CGImageRef screenShot = CGWindowListCreateImage (CGRectNull,
  584. kCGWindowListOptionIncludingWindow,
  585. (CGWindowID) [nsWindow windowNumber],
  586. kCGWindowImageBoundsIgnoreFraming);
  587. NSBitmapImageRep* bitmapRep = [[NSBitmapImageRep alloc] initWithCGImage: screenShot];
  588. Image result (Image::ARGB, (int) [bitmapRep size].width, (int) [bitmapRep size].height, true);
  589. selectImageForDrawing (result);
  590. [bitmapRep drawAtPoint: NSMakePoint (0, 0)];
  591. releaseImageAfterDrawing();
  592. [bitmapRep release];
  593. CGImageRelease (screenShot);
  594. return result;
  595. }
  596. }
  597. Image createSnapshotOfNativeWindow (void* nativeWindowHandle)
  598. {
  599. if (id windowOrView = (id) nativeWindowHandle)
  600. {
  601. if ([windowOrView isKindOfClass: [NSWindow class]])
  602. return createNSWindowSnapshot ((NSWindow*) windowOrView);
  603. if ([windowOrView isKindOfClass: [NSView class]])
  604. return createNSWindowSnapshot ([(NSView*) windowOrView window]);
  605. }
  606. return {};
  607. }
  608. //==============================================================================
  609. void SystemClipboard::copyTextToClipboard (const String& text)
  610. {
  611. NSPasteboard* pb = [NSPasteboard generalPasteboard];
  612. [pb declareTypes: [NSArray arrayWithObject: NSPasteboardTypeString]
  613. owner: nil];
  614. [pb setString: juceStringToNS (text)
  615. forType: NSPasteboardTypeString];
  616. }
  617. String SystemClipboard::getTextFromClipboard()
  618. {
  619. return nsStringToJuce ([[NSPasteboard generalPasteboard] stringForType: NSPasteboardTypeString]);
  620. }
  621. void Process::setDockIconVisible (bool isVisible)
  622. {
  623. ProcessSerialNumber psn { 0, kCurrentProcess };
  624. OSStatus err = TransformProcessType (&psn, isVisible ? kProcessTransformToForegroundApplication
  625. : kProcessTransformToUIElementApplication);
  626. jassert (err == 0);
  627. ignoreUnused (err);
  628. }
  629. } // namespace juce