Add remaining files
[juce-lv2.git] / juce / source / src / native / mac / juce_mac_NSViewComponentPeer.mm
blobbd5dd7f4cb1a33b1e5e09f953c3e1d1681eef980
1 /*\r
2   ==============================================================================\r
3 \r
4    This file is part of the JUCE library - "Jules' Utility Class Extensions"\r
5    Copyright 2004-11 by Raw Material Software Ltd.\r
6 \r
7   ------------------------------------------------------------------------------\r
8 \r
9    JUCE can be redistributed and/or modified under the terms of the GNU General\r
10    Public License (Version 2), as published by the Free Software Foundation.\r
11    A copy of the license is included in the JUCE distribution, or can be found\r
12    online at www.gnu.org/licenses.\r
14    JUCE is distributed in the hope that it will be useful, but WITHOUT ANY\r
15    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r
16    A PARTICULAR PURPOSE.  See the GNU General Public License for more details.\r
18   ------------------------------------------------------------------------------\r
20    To release a closed-source product which uses JUCE, commercial licenses are\r
21    available: visit www.rawmaterialsoftware.com/juce for more information.\r
23   ==============================================================================\r
24 */\r
26 // (This file gets included by juce_mac_NativeCode.mm, rather than being\r
27 // compiled on its own).\r
28 #if JUCE_INCLUDED_FILE\r
30 class NSViewComponentPeer;\r
32 //==============================================================================\r
33 END_JUCE_NAMESPACE\r
35 @interface NSEvent (JuceDeviceDelta)\r
36  - (float) deviceDeltaX;\r
37  - (float) deviceDeltaY;\r
38 @end\r
40 #define JuceNSView MakeObjCClassName(JuceNSView)\r
42 @interface JuceNSView : NSView<NSTextInput>\r
43 {\r
44 @public\r
45     NSViewComponentPeer* owner;\r
46     NSNotificationCenter* notificationCenter;\r
47     String* stringBeingComposed;\r
48     bool textWasInserted;\r
49 }\r
51 - (JuceNSView*) initWithOwner: (NSViewComponentPeer*) owner withFrame: (NSRect) frame;\r
52 - (void) dealloc;\r
54 - (BOOL) isOpaque;\r
55 - (void) drawRect: (NSRect) r;\r
57 - (void) mouseDown: (NSEvent*) ev;\r
58 - (void) asyncMouseDown: (NSEvent*) ev;\r
59 - (void) mouseUp: (NSEvent*) ev;\r
60 - (void) asyncMouseUp: (NSEvent*) ev;\r
61 - (void) mouseDragged: (NSEvent*) ev;\r
62 - (void) mouseMoved: (NSEvent*) ev;\r
63 - (void) mouseEntered: (NSEvent*) ev;\r
64 - (void) mouseExited: (NSEvent*) ev;\r
65 - (void) rightMouseDown: (NSEvent*) ev;\r
66 - (void) rightMouseDragged: (NSEvent*) ev;\r
67 - (void) rightMouseUp: (NSEvent*) ev;\r
68 - (void) otherMouseDown: (NSEvent*) ev;\r
69 - (void) otherMouseDragged: (NSEvent*) ev;\r
70 - (void) otherMouseUp: (NSEvent*) ev;\r
71 - (void) scrollWheel: (NSEvent*) ev;\r
72 - (BOOL) acceptsFirstMouse: (NSEvent*) ev;\r
73 - (void) frameChanged: (NSNotification*) n;\r
74 - (void) viewDidMoveToWindow;\r
76 - (void) keyDown: (NSEvent*) ev;\r
77 - (void) keyUp: (NSEvent*) ev;\r
79 // NSTextInput Methods\r
80 - (void) insertText: (id) aString;\r
81 - (void) doCommandBySelector: (SEL) aSelector;\r
82 - (void) setMarkedText: (id) aString selectedRange: (NSRange) selRange;\r
83 - (void) unmarkText;\r
84 - (BOOL) hasMarkedText;\r
85 - (long) conversationIdentifier;\r
86 - (NSAttributedString*) attributedSubstringFromRange: (NSRange) theRange;\r
87 - (NSRange) markedRange;\r
88 - (NSRange) selectedRange;\r
89 - (NSRect) firstRectForCharacterRange: (NSRange) theRange;\r
90 - (NSUInteger) characterIndexForPoint: (NSPoint) thePoint;\r
91 - (NSArray*) validAttributesForMarkedText;\r
93 - (void) flagsChanged: (NSEvent*) ev;\r
94 #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5\r
95 - (BOOL) performKeyEquivalent: (NSEvent*) ev;\r
96 #endif\r
98 - (BOOL) becomeFirstResponder;\r
99 - (BOOL) resignFirstResponder;\r
100 - (BOOL) acceptsFirstResponder;\r
102 - (void) asyncRepaint: (id) rect;\r
104 - (NSArray*) getSupportedDragTypes;\r
105 - (BOOL) sendDragCallback: (int) type sender: (id <NSDraggingInfo>) sender;\r
106 - (NSDragOperation) draggingEntered: (id <NSDraggingInfo>) sender;\r
107 - (NSDragOperation) draggingUpdated: (id <NSDraggingInfo>) sender;\r
108 - (void) draggingEnded: (id <NSDraggingInfo>) sender;\r
109 - (void) draggingExited: (id <NSDraggingInfo>) sender;\r
110 - (BOOL) prepareForDragOperation: (id <NSDraggingInfo>) sender;\r
111 - (BOOL) performDragOperation: (id <NSDraggingInfo>) sender;\r
112 - (void) concludeDragOperation: (id <NSDraggingInfo>) sender;\r
114 @end\r
116 //==============================================================================\r
117 #define JuceNSWindow MakeObjCClassName(JuceNSWindow)\r
119 #if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6\r
120 @interface JuceNSWindow : NSWindow <NSWindowDelegate>\r
121 #else\r
122 @interface JuceNSWindow : NSWindow\r
123 #endif\r
125 @private\r
126     NSViewComponentPeer* owner;\r
127     bool isZooming;\r
130 - (void) setOwner: (NSViewComponentPeer*) owner;\r
131 - (BOOL) canBecomeKeyWindow;\r
132 - (void) becomeKeyWindow;\r
133 - (BOOL) windowShouldClose: (id) window;\r
134 - (NSRect) constrainFrameRect: (NSRect) frameRect toScreen: (NSScreen*) screen;\r
135 - (NSSize) windowWillResize: (NSWindow*) window toSize: (NSSize) proposedFrameSize;\r
136 - (void) zoom: (id) sender;\r
137 @end\r
139 BEGIN_JUCE_NAMESPACE\r
141 //==============================================================================\r
142 class NSViewComponentPeer  : public ComponentPeer\r
144 public:\r
145     NSViewComponentPeer (Component* const component,\r
146                          const int windowStyleFlags,\r
147                          NSView* viewToAttachTo);\r
149     ~NSViewComponentPeer();\r
151     //==============================================================================\r
152     void* getNativeHandle() const;\r
153     void setVisible (bool shouldBeVisible);\r
154     void setTitle (const String& title);\r
155     void setPosition (int x, int y);\r
156     void setSize (int w, int h);\r
157     void setBounds (int x, int y, int w, int h, const bool isNowFullScreen);\r
158     const Rectangle<int> getBounds (const bool global) const;\r
159     const Rectangle<int> getBounds() const;\r
160     const Point<int> getScreenPosition() const;\r
161     const Point<int> localToGlobal (const Point<int>& relativePosition);\r
162     const Point<int> globalToLocal (const Point<int>& screenPosition);\r
163     void setAlpha (float newAlpha);\r
164     void setMinimised (bool shouldBeMinimised);\r
165     bool isMinimised() const;\r
166     void setFullScreen (bool shouldBeFullScreen);\r
167     bool isFullScreen() const;\r
168     void updateFullscreenStatus();\r
169     bool contains (const Point<int>& position, bool trueIfInAChildWindow) const;\r
170     bool hasNativeTitleBar() const        { return (getStyleFlags() & windowHasTitleBar) != 0; }\r
171     const BorderSize<int> getFrameSize() const;\r
172     bool setAlwaysOnTop (bool alwaysOnTop);\r
173     void toFront (bool makeActiveWindow);\r
174     void toBehind (ComponentPeer* other);\r
175     void setIcon (const Image& newIcon);\r
176     StringArray getAvailableRenderingEngines();\r
177     int getCurrentRenderingEngine() const;\r
178     void setCurrentRenderingEngine (int index);\r
180     /* When you use multiple DLLs which share similarly-named obj-c classes - like\r
181        for example having more than one juce plugin loaded into a host, then when a\r
182        method is called, the actual code that runs might actually be in a different module\r
183        than the one you expect... So any calls to library functions or statics that are\r
184        made inside obj-c methods will probably end up getting executed in a different DLL's\r
185        memory space. Not a great thing to happen - this obviously leads to bizarre crashes.\r
187        To work around this insanity, I'm only allowing obj-c methods to make calls to\r
188        virtual methods of an object that's known to live inside the right module's space.\r
189     */\r
190     virtual void redirectMouseDown (NSEvent* ev);\r
191     virtual void redirectMouseUp (NSEvent* ev);\r
192     virtual void redirectMouseDrag (NSEvent* ev);\r
193     virtual void redirectMouseMove (NSEvent* ev);\r
194     virtual void redirectMouseEnter (NSEvent* ev);\r
195     virtual void redirectMouseExit (NSEvent* ev);\r
196     virtual void redirectMouseWheel (NSEvent* ev);\r
197     void sendMouseEvent (NSEvent* ev);\r
199     bool handleKeyEvent (NSEvent* ev, bool isKeyDown);\r
200     virtual bool redirectKeyDown (NSEvent* ev);\r
201     virtual bool redirectKeyUp (NSEvent* ev);\r
202     virtual void redirectModKeyChange (NSEvent* ev);\r
203    #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5\r
204     virtual bool redirectPerformKeyEquivalent (NSEvent* ev);\r
205    #endif\r
207     virtual BOOL sendDragCallback (int type, id <NSDraggingInfo> sender);\r
209     virtual bool isOpaque();\r
210     virtual void drawRect (NSRect r);\r
212     virtual bool canBecomeKeyWindow();\r
213     virtual void becomeKeyWindow();\r
214     virtual bool windowShouldClose();\r
216     virtual void redirectMovedOrResized();\r
217     virtual void viewMovedToWindow();\r
219     virtual NSRect constrainRect (NSRect r);\r
221     static void showArrowCursorIfNeeded();\r
222     static void updateModifiers (NSEvent* e);\r
223     static void updateKeysDown (NSEvent* ev, bool isKeyDown);\r
225     static int getKeyCodeFromEvent (NSEvent* ev)\r
226     {\r
227         const String unmodified (nsStringToJuce ([ev charactersIgnoringModifiers]));\r
228         int keyCode = unmodified[0];\r
230         if (keyCode == 0x19) // (backwards-tab)\r
231             keyCode = '\t';\r
232         else if (keyCode == 0x03) // (enter)\r
233             keyCode = '\r';\r
234         else\r
235             keyCode = (int) CharacterFunctions::toUpperCase ((juce_wchar) keyCode);\r
237         if (([ev modifierFlags] & NSNumericPadKeyMask) != 0)\r
238         {\r
239             const int numPadConversions[] = { '0', KeyPress::numberPad0, '1', KeyPress::numberPad1,\r
240                                               '2', KeyPress::numberPad2, '3', KeyPress::numberPad3,\r
241                                               '4', KeyPress::numberPad4, '5', KeyPress::numberPad5,\r
242                                               '6', KeyPress::numberPad6, '7', KeyPress::numberPad7,\r
243                                               '8', KeyPress::numberPad8, '9', KeyPress::numberPad9,\r
244                                               '+', KeyPress::numberPadAdd,  '-', KeyPress::numberPadSubtract,\r
245                                               '*', KeyPress::numberPadMultiply, '/', KeyPress::numberPadDivide,\r
246                                               '.', KeyPress::numberPadDecimalPoint, '=', KeyPress::numberPadEquals };\r
248             for (int i = 0; i < numElementsInArray (numPadConversions); i += 2)\r
249                 if (keyCode == numPadConversions [i])\r
250                     keyCode = numPadConversions [i + 1];\r
251         }\r
253         return keyCode;\r
254     }\r
256     static int64 getMouseTime (NSEvent* e)\r
257     {\r
258         return (Time::currentTimeMillis() - Time::getMillisecondCounter())\r
259                 + (int64) ([e timestamp] * 1000.0);\r
260     }\r
262     static const Point<int> getMousePos (NSEvent* e, NSView* view)\r
263     {\r
264         NSPoint p = [view convertPoint: [e locationInWindow] fromView: nil];\r
265         return Point<int> (roundToInt (p.x), roundToInt ([view frame].size.height - p.y));\r
266     }\r
268     static int getModifierForButtonNumber (const NSInteger num)\r
269     {\r
270         return num == 0 ? ModifierKeys::leftButtonModifier\r
271                     : (num == 1 ? ModifierKeys::rightButtonModifier\r
272                                 : (num == 2 ? ModifierKeys::middleButtonModifier : 0));\r
273     }\r
275     static unsigned int getNSWindowStyleMask (const int flags) noexcept\r
276     {\r
277         unsigned int style = (flags & windowHasTitleBar) != 0 ? NSTitledWindowMask\r
278                                                               : NSBorderlessWindowMask;\r
280         if ((flags & windowHasMinimiseButton) != 0)  style |= NSMiniaturizableWindowMask;\r
281         if ((flags & windowHasCloseButton) != 0)     style |= NSClosableWindowMask;\r
282         if ((flags & windowIsResizable) != 0)        style |= NSResizableWindowMask;\r
283         return style;\r
284     }\r
286     //==============================================================================\r
287     virtual void viewFocusGain();\r
288     virtual void viewFocusLoss();\r
289     bool isFocused() const;\r
290     void grabFocus();\r
291     void textInputRequired (const Point<int>& position);\r
293     //==============================================================================\r
294     void repaint (const Rectangle<int>& area);\r
295     void performAnyPendingRepaintsNow();\r
297     //==============================================================================\r
298     NSWindow* window;\r
299     JuceNSView* view;\r
300     bool isSharedWindow, fullScreen, insideDrawRect, usingCoreGraphics, recursiveToFrontCall;\r
302     static ModifierKeys currentModifiers;\r
303     static ComponentPeer* currentlyFocusedPeer;\r
304     static Array<int> keysCurrentlyDown;\r
306 private:\r
307     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NSViewComponentPeer);\r
308 };\r
310 //==============================================================================\r
311 END_JUCE_NAMESPACE\r
313 @implementation JuceNSView\r
315 - (JuceNSView*) initWithOwner: (NSViewComponentPeer*) owner_\r
316                     withFrame: (NSRect) frame\r
318     [super initWithFrame: frame];\r
319     owner = owner_;\r
320     stringBeingComposed = nullptr;\r
321     textWasInserted = false;\r
323     notificationCenter = [NSNotificationCenter defaultCenter];\r
325     [notificationCenter  addObserver: self\r
326                             selector: @selector (frameChanged:)\r
327                                 name: NSViewFrameDidChangeNotification\r
328                               object: self];\r
330     if (! owner_->isSharedWindow)\r
331     {\r
332         [notificationCenter  addObserver: self\r
333                                 selector: @selector (frameChanged:)\r
334                                     name: NSWindowDidMoveNotification\r
335                                   object: owner_->window];\r
336     }\r
338     [self registerForDraggedTypes: [self getSupportedDragTypes]];\r
340     return self;\r
343 - (void) dealloc\r
345     [notificationCenter removeObserver: self];\r
346     delete stringBeingComposed;\r
347     [super dealloc];\r
350 //==============================================================================\r
351 - (void) drawRect: (NSRect) r\r
353     if (owner != nullptr)\r
354         owner->drawRect (r);\r
357 - (BOOL) isOpaque\r
359     return owner == nullptr || owner->isOpaque();\r
362 //==============================================================================\r
363 - (void) mouseDown: (NSEvent*) ev\r
365     if (JUCEApplication::isStandaloneApp())\r
366         [self asyncMouseDown: ev];\r
367     else\r
368         // In some host situations, the host will stop modal loops from working\r
369         // correctly if they're called from a mouse event, so we'll trigger\r
370         // the event asynchronously..\r
371         [self performSelectorOnMainThread: @selector (asyncMouseDown:)\r
372                                withObject: ev\r
373                             waitUntilDone: NO];\r
376 - (void) asyncMouseDown: (NSEvent*) ev\r
378     if (owner != nullptr)\r
379         owner->redirectMouseDown (ev);\r
382 - (void) mouseUp: (NSEvent*) ev\r
384     if (! JUCEApplication::isStandaloneApp())\r
385         [self asyncMouseUp: ev];\r
386     else\r
387         // In some host situations, the host will stop modal loops from working\r
388         // correctly if they're called from a mouse event, so we'll trigger\r
389         // the event asynchronously..\r
390         [self performSelectorOnMainThread: @selector (asyncMouseUp:)\r
391                                withObject: ev\r
392                             waitUntilDone: NO];\r
395 - (void) asyncMouseUp: (NSEvent*) ev    { if (owner != nullptr) owner->redirectMouseUp    (ev); }\r
396 - (void) mouseDragged: (NSEvent*) ev    { if (owner != nullptr) owner->redirectMouseDrag  (ev); }\r
397 - (void) mouseMoved:   (NSEvent*) ev    { if (owner != nullptr) owner->redirectMouseMove  (ev); }\r
398 - (void) mouseEntered: (NSEvent*) ev    { if (owner != nullptr) owner->redirectMouseEnter (ev); }\r
399 - (void) mouseExited:  (NSEvent*) ev    { if (owner != nullptr) owner->redirectMouseExit  (ev); }\r
400 - (void) scrollWheel:  (NSEvent*) ev    { if (owner != nullptr) owner->redirectMouseWheel (ev); }\r
402 - (void) rightMouseDown:    (NSEvent*) ev   { [self mouseDown:    ev]; }\r
403 - (void) rightMouseDragged: (NSEvent*) ev   { [self mouseDragged: ev]; }\r
404 - (void) rightMouseUp:      (NSEvent*) ev   { [self mouseUp:      ev]; }\r
405 - (void) otherMouseDown:    (NSEvent*) ev   { [self mouseDown:    ev]; }\r
406 - (void) otherMouseDragged: (NSEvent*) ev   { [self mouseDragged: ev]; }\r
407 - (void) otherMouseUp:      (NSEvent*) ev   { [self mouseUp:      ev]; }\r
409 - (BOOL) acceptsFirstMouse: (NSEvent*) ev\r
411     (void) ev;\r
412     return YES;\r
415 - (void) frameChanged: (NSNotification*) n\r
417     (void) n;\r
418     if (owner != nullptr)\r
419         owner->redirectMovedOrResized();\r
422 - (void) viewDidMoveToWindow\r
424    if (owner != nullptr)\r
425        owner->viewMovedToWindow();\r
428 - (void) asyncRepaint: (id) rect\r
430     NSRect* r = (NSRect*) [((NSData*) rect) bytes];\r
431     [self setNeedsDisplayInRect: *r];\r
434 //==============================================================================\r
435 - (void) keyDown: (NSEvent*) ev\r
437     TextInputTarget* const target = owner->findCurrentTextInputTarget();\r
438     textWasInserted = false;\r
440     if (target != nullptr)\r
441         [self interpretKeyEvents: [NSArray arrayWithObject: ev]];\r
442     else\r
443         deleteAndZero (stringBeingComposed);\r
445     if ((! textWasInserted) && (owner == nullptr || ! owner->redirectKeyDown (ev)))\r
446         [super keyDown: ev];\r
449 - (void) keyUp: (NSEvent*) ev\r
451     if (owner == nullptr || ! owner->redirectKeyUp (ev))\r
452         [super keyUp: ev];\r
455 //==============================================================================\r
456 - (void) insertText: (id) aString\r
458     // This commits multi-byte text when return is pressed, or after every keypress for western keyboards\r
459     NSString* newText = [aString isKindOfClass: [NSAttributedString class]] ? [aString string] : aString;\r
461     if ([newText length] > 0)\r
462     {\r
463         TextInputTarget* const target = owner->findCurrentTextInputTarget();\r
465         if (target != nullptr)\r
466         {\r
467             target->insertTextAtCaret (nsStringToJuce (newText));\r
468             textWasInserted = true;\r
469         }\r
470     }\r
472     deleteAndZero (stringBeingComposed);\r
475 - (void) doCommandBySelector: (SEL) aSelector\r
477     (void) aSelector;\r
480 - (void) setMarkedText: (id) aString selectedRange: (NSRange) selectionRange\r
482     (void) selectionRange;\r
484     if (stringBeingComposed == 0)\r
485         stringBeingComposed = new String();\r
487     *stringBeingComposed = nsStringToJuce ([aString isKindOfClass:[NSAttributedString class]] ? [aString string] : aString);\r
489     TextInputTarget* const target = owner->findCurrentTextInputTarget();\r
491     if (target != nullptr)\r
492     {\r
493         const Range<int> currentHighlight (target->getHighlightedRegion());\r
494         target->insertTextAtCaret (*stringBeingComposed);\r
495         target->setHighlightedRegion (currentHighlight.withLength (stringBeingComposed->length()));\r
496         textWasInserted = true;\r
497     }\r
500 - (void) unmarkText\r
502     if (stringBeingComposed != nullptr)\r
503     {\r
504         TextInputTarget* const target = owner->findCurrentTextInputTarget();\r
506         if (target != nullptr)\r
507         {\r
508             target->insertTextAtCaret (*stringBeingComposed);\r
509             textWasInserted = true;\r
510         }\r
511     }\r
513     deleteAndZero (stringBeingComposed);\r
516 - (BOOL) hasMarkedText\r
518     return stringBeingComposed != nullptr;\r
521 - (long) conversationIdentifier\r
523     return (long) (pointer_sized_int) self;\r
526 - (NSAttributedString*) attributedSubstringFromRange: (NSRange) theRange\r
528     TextInputTarget* const target = owner->findCurrentTextInputTarget();\r
530     if (target != nullptr)\r
531     {\r
532         const Range<int> r ((int) theRange.location,\r
533                             (int) (theRange.location + theRange.length));\r
535         return [[[NSAttributedString alloc] initWithString: juceStringToNS (target->getTextInRange (r))] autorelease];\r
536     }\r
538     return nil;\r
541 - (NSRange) markedRange\r
543     return stringBeingComposed != nullptr ? NSMakeRange (0, stringBeingComposed->length())\r
544                                           : NSMakeRange (NSNotFound, 0);\r
547 - (NSRange) selectedRange\r
549     TextInputTarget* const target = owner->findCurrentTextInputTarget();\r
551     if (target != nullptr)\r
552     {\r
553         const Range<int> highlight (target->getHighlightedRegion());\r
555         if (! highlight.isEmpty())\r
556             return NSMakeRange (highlight.getStart(), highlight.getLength());\r
557     }\r
559     return NSMakeRange (NSNotFound, 0);\r
562 - (NSRect) firstRectForCharacterRange: (NSRange) theRange\r
564     (void) theRange;\r
565     JUCE_NAMESPACE::Component* const comp = dynamic_cast <JUCE_NAMESPACE::Component*> (owner->findCurrentTextInputTarget());\r
567     if (comp == 0)\r
568         return NSMakeRect (0, 0, 0, 0);\r
570     const Rectangle<int> bounds (comp->getScreenBounds());\r
572     return NSMakeRect (bounds.getX(),\r
573                        [[[NSScreen screens] objectAtIndex: 0] frame].size.height - bounds.getY(),\r
574                        bounds.getWidth(),\r
575                        bounds.getHeight());\r
578 - (NSUInteger) characterIndexForPoint: (NSPoint) thePoint\r
580     (void) thePoint;\r
581     return NSNotFound;\r
584 - (NSArray*) validAttributesForMarkedText\r
586     return [NSArray array];\r
589 //==============================================================================\r
590 - (void) flagsChanged: (NSEvent*) ev\r
592     if (owner != nullptr)\r
593         owner->redirectModKeyChange (ev);\r
596 #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5\r
597 - (BOOL) performKeyEquivalent: (NSEvent*) ev\r
599     if (owner != nullptr && owner->redirectPerformKeyEquivalent (ev))\r
600         return true;\r
602     return [super performKeyEquivalent: ev];\r
604 #endif\r
606 - (BOOL) becomeFirstResponder   { if (owner != nullptr) owner->viewFocusGain(); return YES; }\r
607 - (BOOL) resignFirstResponder   { if (owner != nullptr) owner->viewFocusLoss(); return YES; }\r
609 - (BOOL) acceptsFirstResponder  { return owner != nullptr && owner->canBecomeKeyWindow(); }\r
611 //==============================================================================\r
612 - (NSArray*) getSupportedDragTypes\r
614     return [NSArray arrayWithObjects: NSFilenamesPboardType, NSFilesPromisePboardType, /* NSStringPboardType,*/ nil];\r
617 - (BOOL) sendDragCallback: (int) type sender: (id <NSDraggingInfo>) sender\r
619     return owner != nullptr && owner->sendDragCallback (type, sender);\r
622 - (NSDragOperation) draggingEntered: (id <NSDraggingInfo>) sender\r
624     if ([self sendDragCallback: 0 sender: sender])\r
625         return NSDragOperationCopy | NSDragOperationMove | NSDragOperationGeneric;\r
626     else\r
627         return NSDragOperationNone;\r
630 - (NSDragOperation) draggingUpdated: (id <NSDraggingInfo>) sender\r
632     if ([self sendDragCallback: 0 sender: sender])\r
633         return NSDragOperationCopy | NSDragOperationMove | NSDragOperationGeneric;\r
634     else\r
635         return NSDragOperationNone;\r
638 - (void) draggingEnded: (id <NSDraggingInfo>) sender\r
640     [self sendDragCallback: 1 sender: sender];\r
643 - (void) draggingExited: (id <NSDraggingInfo>) sender\r
645     [self sendDragCallback: 1 sender: sender];\r
648 - (BOOL) prepareForDragOperation: (id <NSDraggingInfo>) sender\r
650     (void) sender;\r
651     return YES;\r
654 - (BOOL) performDragOperation: (id <NSDraggingInfo>) sender\r
656     return [self sendDragCallback: 2 sender: sender];\r
659 - (void) concludeDragOperation: (id <NSDraggingInfo>) sender\r
661     (void) sender;\r
664 @end\r
666 //==============================================================================\r
667 @implementation JuceNSWindow\r
669 - (void) setOwner: (NSViewComponentPeer*) owner_\r
671     owner = owner_;\r
672     isZooming = false;\r
675 - (BOOL) canBecomeKeyWindow\r
677     return owner != nullptr && owner->canBecomeKeyWindow();\r
680 - (void) becomeKeyWindow\r
682     [super becomeKeyWindow];\r
684     if (owner != nullptr)\r
685         owner->becomeKeyWindow();\r
688 - (BOOL) windowShouldClose: (id) window\r
690     (void) window;\r
691     return owner == nullptr || owner->windowShouldClose();\r
694 - (NSRect) constrainFrameRect: (NSRect) frameRect toScreen: (NSScreen*) screen\r
696     (void) screen;\r
697     if (owner != nullptr)\r
698         frameRect = owner->constrainRect (frameRect);\r
700     return frameRect;\r
703 - (NSSize) windowWillResize: (NSWindow*) window toSize: (NSSize) proposedFrameSize\r
705     (void) window;\r
706     if (isZooming)\r
707         return proposedFrameSize;\r
709     NSRect frameRect = [self frame];\r
710     frameRect.origin.y -= proposedFrameSize.height - frameRect.size.height;\r
711     frameRect.size = proposedFrameSize;\r
713     if (owner != nullptr)\r
714         frameRect = owner->constrainRect (frameRect);\r
716     if (JUCE_NAMESPACE::Component::getCurrentlyModalComponent() != nullptr\r
717           && owner->getComponent()->isCurrentlyBlockedByAnotherModalComponent()\r
718           && owner->hasNativeTitleBar())\r
719         JUCE_NAMESPACE::Component::getCurrentlyModalComponent()->inputAttemptWhenModal();\r
721     return frameRect.size;\r
724 - (void) zoom: (id) sender\r
726     isZooming = true;\r
727     [super zoom: sender];\r
728     isZooming = false;\r
730     owner->redirectMovedOrResized();\r
733 - (void) windowWillMove: (NSNotification*) notification\r
735     (void) notification;\r
737     if (JUCE_NAMESPACE::Component::getCurrentlyModalComponent() != nullptr\r
738           && owner->getComponent()->isCurrentlyBlockedByAnotherModalComponent()\r
739           && owner->hasNativeTitleBar())\r
740         JUCE_NAMESPACE::Component::getCurrentlyModalComponent()->inputAttemptWhenModal();\r
743 @end\r
745 //==============================================================================\r
746 //==============================================================================\r
747 BEGIN_JUCE_NAMESPACE\r
749 //==============================================================================\r
750 ModifierKeys NSViewComponentPeer::currentModifiers;\r
751 ComponentPeer* NSViewComponentPeer::currentlyFocusedPeer = nullptr;\r
752 Array<int> NSViewComponentPeer::keysCurrentlyDown;\r
754 //==============================================================================\r
755 bool KeyPress::isKeyCurrentlyDown (const int keyCode)\r
757     if (NSViewComponentPeer::keysCurrentlyDown.contains (keyCode))\r
758         return true;\r
760     if (keyCode >= 'A' && keyCode <= 'Z'\r
761          && NSViewComponentPeer::keysCurrentlyDown.contains ((int) CharacterFunctions::toLowerCase ((juce_wchar) keyCode)))\r
762         return true;\r
764     if (keyCode >= 'a' && keyCode <= 'z'\r
765          && NSViewComponentPeer::keysCurrentlyDown.contains ((int) CharacterFunctions::toUpperCase ((juce_wchar) keyCode)))\r
766         return true;\r
768     return false;\r
771 void NSViewComponentPeer::updateModifiers (NSEvent* e)\r
773     int m = 0;\r
775     if (([e modifierFlags] & NSShiftKeyMask) != 0)          m |= ModifierKeys::shiftModifier;\r
776     if (([e modifierFlags] & NSControlKeyMask) != 0)        m |= ModifierKeys::ctrlModifier;\r
777     if (([e modifierFlags] & NSAlternateKeyMask) != 0)      m |= ModifierKeys::altModifier;\r
778     if (([e modifierFlags] & NSCommandKeyMask) != 0)        m |= ModifierKeys::commandModifier;\r
780     currentModifiers = currentModifiers.withOnlyMouseButtons().withFlags (m);\r
783 void NSViewComponentPeer::updateKeysDown (NSEvent* ev, bool isKeyDown)\r
785     updateModifiers (ev);\r
786     int keyCode = getKeyCodeFromEvent (ev);\r
788     if (keyCode != 0)\r
789     {\r
790         if (isKeyDown)\r
791             keysCurrentlyDown.addIfNotAlreadyThere (keyCode);\r
792         else\r
793             keysCurrentlyDown.removeValue (keyCode);\r
794     }\r
797 const ModifierKeys ModifierKeys::getCurrentModifiersRealtime() noexcept\r
799     return NSViewComponentPeer::currentModifiers;\r
802 void ModifierKeys::updateCurrentModifiers() noexcept\r
804     currentModifiers = NSViewComponentPeer::currentModifiers;\r
807 //==============================================================================\r
808 NSViewComponentPeer::NSViewComponentPeer (Component* const component_,\r
809                                           const int windowStyleFlags,\r
810                                           NSView* viewToAttachTo)\r
811     : ComponentPeer (component_, windowStyleFlags),\r
812       window (nil),\r
813       view (nil),\r
814       isSharedWindow (viewToAttachTo != nil),\r
815       fullScreen (false),\r
816       insideDrawRect (false),\r
817      #if USE_COREGRAPHICS_RENDERING\r
818       usingCoreGraphics (true),\r
819      #else\r
820       usingCoreGraphics (false),\r
821      #endif\r
822       recursiveToFrontCall (false)\r
824     NSRect r = NSMakeRect (0, 0, (CGFloat) component->getWidth(), (CGFloat) component->getHeight());\r
826     view = [[JuceNSView alloc] initWithOwner: this withFrame: r];\r
827     [view setPostsFrameChangedNotifications: YES];\r
829     if (isSharedWindow)\r
830     {\r
831         window = [viewToAttachTo window];\r
832         [viewToAttachTo addSubview: view];\r
833     }\r
834     else\r
835     {\r
836         r.origin.x = (CGFloat) component->getX();\r
837         r.origin.y = (CGFloat) component->getY();\r
838         r.origin.y = [[[NSScreen screens] objectAtIndex: 0] frame].size.height - (r.origin.y + r.size.height);\r
840         window = [[JuceNSWindow alloc] initWithContentRect: r\r
841                                                  styleMask: getNSWindowStyleMask (windowStyleFlags)\r
842                                                    backing: NSBackingStoreBuffered\r
843                                                      defer: YES];\r
845         [((JuceNSWindow*) window) setOwner: this];\r
846         [window orderOut: nil];\r
847         [window setDelegate: (JuceNSWindow*) window];\r
848         [window setOpaque: component->isOpaque()];\r
849         [window setHasShadow: ((windowStyleFlags & windowHasDropShadow) != 0)];\r
851         if (component->isAlwaysOnTop())\r
852             [window setLevel: NSFloatingWindowLevel];\r
854         [window setContentView: view];\r
855         [window setAutodisplay: YES];\r
856         [window setAcceptsMouseMovedEvents: YES];\r
858         // We'll both retain and also release this on closing because plugin hosts can unexpectedly\r
859         // close the window for us, and also tend to get cause trouble if setReleasedWhenClosed is NO.\r
860         [window setReleasedWhenClosed: YES];\r
861         [window retain];\r
863         [window setExcludedFromWindowsMenu: (windowStyleFlags & windowIsTemporary) != 0];\r
864         [window setIgnoresMouseEvents: (windowStyleFlags & windowIgnoresMouseClicks) != 0];\r
865     }\r
867     const float alpha = component->getAlpha();\r
868     if (alpha < 1.0f)\r
869         setAlpha (alpha);\r
871     setTitle (component->getName());\r
874 NSViewComponentPeer::~NSViewComponentPeer()\r
876     view->owner = nullptr;\r
877     [view removeFromSuperview];\r
878     [view release];\r
880     if (! isSharedWindow)\r
881     {\r
882         [((JuceNSWindow*) window) setOwner: 0];\r
883         [window close];\r
884         [window release];\r
885     }\r
888 //==============================================================================\r
889 void* NSViewComponentPeer::getNativeHandle() const\r
891     return view;\r
894 void NSViewComponentPeer::setVisible (bool shouldBeVisible)\r
896     if (isSharedWindow)\r
897     {\r
898         [view setHidden: ! shouldBeVisible];\r
899     }\r
900     else\r
901     {\r
902         if (shouldBeVisible)\r
903         {\r
904             [window orderFront: nil];\r
905             handleBroughtToFront();\r
906         }\r
907         else\r
908         {\r
909             [window orderOut: nil];\r
910         }\r
911     }\r
914 void NSViewComponentPeer::setTitle (const String& title)\r
916     JUCE_AUTORELEASEPOOL\r
918     if (! isSharedWindow)\r
919         [window setTitle: juceStringToNS (title)];\r
922 void NSViewComponentPeer::setPosition (int x, int y)\r
924     setBounds (x, y, component->getWidth(), component->getHeight(), false);\r
927 void NSViewComponentPeer::setSize (int w, int h)\r
929     setBounds (component->getX(), component->getY(), w, h, false);\r
932 void NSViewComponentPeer::setBounds (int x, int y, int w, int h, bool isNowFullScreen)\r
934     fullScreen = isNowFullScreen;\r
936     NSRect r = NSMakeRect ((CGFloat) x, (CGFloat) y, (CGFloat) jmax (0, w), (CGFloat) jmax (0, h));\r
938     if (isSharedWindow)\r
939     {\r
940         r.origin.y = [[view superview] frame].size.height - (r.origin.y + r.size.height);\r
942         if ([view frame].size.width != r.size.width\r
943              || [view frame].size.height != r.size.height)\r
944             [view setNeedsDisplay: true];\r
946         [view setFrame: r];\r
947     }\r
948     else\r
949     {\r
950         r.origin.y = [[[NSScreen screens] objectAtIndex: 0] frame].size.height - (r.origin.y + r.size.height);\r
952         [window setFrame: [window frameRectForContentRect: r]\r
953                  display: true];\r
954     }\r
957 const Rectangle<int> NSViewComponentPeer::getBounds (const bool global) const\r
959     NSRect r = [view frame];\r
961     if (global && [view window] != nil)\r
962     {\r
963         r = [view convertRect: r toView: nil];\r
964         NSRect wr = [[view window] frame];\r
965         r.origin.x += wr.origin.x;\r
966         r.origin.y += wr.origin.y;\r
967         r.origin.y = [[[NSScreen screens] objectAtIndex: 0] frame].size.height - r.origin.y - r.size.height;\r
968     }\r
969     else\r
970     {\r
971         r.origin.y = [[view superview] frame].size.height - r.origin.y - r.size.height;\r
972     }\r
974     return Rectangle<int> (convertToRectInt (r));\r
977 const Rectangle<int> NSViewComponentPeer::getBounds() const\r
979     return getBounds (! isSharedWindow);\r
982 const Point<int> NSViewComponentPeer::getScreenPosition() const\r
984     return getBounds (true).getPosition();\r
987 const Point<int> NSViewComponentPeer::localToGlobal (const Point<int>& relativePosition)\r
989     return relativePosition + getScreenPosition();\r
992 const Point<int> NSViewComponentPeer::globalToLocal (const Point<int>& screenPosition)\r
994     return screenPosition - getScreenPosition();\r
997 NSRect NSViewComponentPeer::constrainRect (NSRect r)\r
999     if (constrainer != nullptr)\r
1000     {\r
1001         NSRect current = [window frame];\r
1002         current.origin.y = [[[NSScreen screens] objectAtIndex: 0] frame].size.height - current.origin.y - current.size.height;\r
1004         r.origin.y = [[[NSScreen screens] objectAtIndex: 0] frame].size.height - r.origin.y - r.size.height;\r
1006         Rectangle<int> pos (convertToRectInt (r));\r
1007         Rectangle<int> original (convertToRectInt (current));\r
1009        #if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MIN_ALLOWED >= MAC_OS_X_VERSION_10_6\r
1010         if ([window inLiveResize])\r
1011        #else\r
1012         if ([window respondsToSelector: @selector (inLiveResize)]\r
1013              && [window performSelector: @selector (inLiveResize)])\r
1014        #endif\r
1015         {\r
1016             constrainer->checkBounds (pos, original,\r
1017                                       Desktop::getInstance().getAllMonitorDisplayAreas().getBounds(),\r
1018                                       false, false, true, true);\r
1019         }\r
1020         else\r
1021         {\r
1022             constrainer->checkBounds (pos, original,\r
1023                                       Desktop::getInstance().getAllMonitorDisplayAreas().getBounds(),\r
1024                                       pos.getY() != original.getY() && pos.getBottom() == original.getBottom(),\r
1025                                       pos.getX() != original.getX() && pos.getRight() == original.getRight(),\r
1026                                       pos.getY() == original.getY() && pos.getBottom() != original.getBottom(),\r
1027                                       pos.getX() == original.getX() && pos.getRight() != original.getRight());\r
1028         }\r
1030         r.origin.x = pos.getX();\r
1031         r.origin.y = [[[NSScreen screens] objectAtIndex: 0] frame].size.height - r.size.height - pos.getY();\r
1032         r.size.width = pos.getWidth();\r
1033         r.size.height = pos.getHeight();\r
1034     }\r
1036     return r;\r
1039 void NSViewComponentPeer::setAlpha (float newAlpha)\r
1041     if (! isSharedWindow)\r
1042     {\r
1043         [window setAlphaValue: (CGFloat) newAlpha];\r
1044     }\r
1045     else\r
1046     {\r
1047        #if defined (MAC_OS_X_VERSION_10_5) && MAC_OS_X_VERSION_MIN_ALLOWED >= MAC_OS_X_VERSION_10_5\r
1048         [view setAlphaValue: (CGFloat) newAlpha];\r
1049        #else\r
1050         if ([view respondsToSelector: @selector (setAlphaValue:)])\r
1051         {\r
1052             // PITA dynamic invocation for 10.4 builds..\r
1053             NSInvocation* inv = [NSInvocation invocationWithMethodSignature: [view methodSignatureForSelector: @selector (setAlphaValue:)]];\r
1054             [inv setSelector: @selector (setAlphaValue:)];\r
1055             [inv setTarget: view];\r
1056             CGFloat cgNewAlpha = (CGFloat) newAlpha;\r
1057             [inv setArgument: &cgNewAlpha atIndex: 2];\r
1058             [inv invoke];\r
1059         }\r
1060        #endif\r
1061     }\r
1064 void NSViewComponentPeer::setMinimised (bool shouldBeMinimised)\r
1066     if (! isSharedWindow)\r
1067     {\r
1068         if (shouldBeMinimised)\r
1069             [window miniaturize: nil];\r
1070         else\r
1071             [window deminiaturize: nil];\r
1072     }\r
1075 bool NSViewComponentPeer::isMinimised() const\r
1077     return [window isMiniaturized];\r
1080 void NSViewComponentPeer::setFullScreen (bool shouldBeFullScreen)\r
1082     if (! isSharedWindow)\r
1083     {\r
1084         Rectangle<int> r (lastNonFullscreenBounds);\r
1086         if (isMinimised())\r
1087             setMinimised (false);\r
1089         if (fullScreen != shouldBeFullScreen)\r
1090         {\r
1091             if (shouldBeFullScreen && hasNativeTitleBar())\r
1092             {\r
1093                 fullScreen = true;\r
1094                 [window performZoom: nil];\r
1095             }\r
1096             else\r
1097             {\r
1098                 if (shouldBeFullScreen)\r
1099                     r = component->getParentMonitorArea();\r
1101                 // (can't call the component's setBounds method because that'll reset our fullscreen flag)\r
1102                 if (r != getComponent()->getBounds() && ! r.isEmpty())\r
1103                     setBounds (r.getX(), r.getY(), r.getWidth(), r.getHeight(), shouldBeFullScreen);\r
1104             }\r
1105         }\r
1106     }\r
1109 bool NSViewComponentPeer::isFullScreen() const\r
1111     return fullScreen;\r
1114 bool NSViewComponentPeer::contains (const Point<int>& position, bool trueIfInAChildWindow) const\r
1116     if (! (isPositiveAndBelow (position.getX(), component->getWidth())\r
1117             && isPositiveAndBelow (position.getY(), component->getHeight())))\r
1118         return false;\r
1120     NSRect frameRect = [view frame];\r
1122     NSView* v = [view hitTest: NSMakePoint (frameRect.origin.x + position.getX(),\r
1123                                             frameRect.origin.y + frameRect.size.height - position.getY())];\r
1125     if (trueIfInAChildWindow)\r
1126         return v != nil;\r
1128     return v == view;\r
1131 const BorderSize<int> NSViewComponentPeer::getFrameSize() const\r
1133     BorderSize<int> b;\r
1135     if (! isSharedWindow)\r
1136     {\r
1137         NSRect v = [view convertRect: [view frame] toView: nil];\r
1138         NSRect w = [window frame];\r
1140         b.setTop ((int) (w.size.height - (v.origin.y + v.size.height)));\r
1141         b.setBottom ((int) v.origin.y);\r
1142         b.setLeft ((int) v.origin.x);\r
1143         b.setRight ((int) (w.size.width - (v.origin.x + v.size.width)));\r
1144     }\r
1146     return b;\r
1149 bool NSViewComponentPeer::setAlwaysOnTop (bool alwaysOnTop)\r
1151     if (! isSharedWindow)\r
1152         [window setLevel: alwaysOnTop ? NSFloatingWindowLevel\r
1153                                       : NSNormalWindowLevel];\r
1154     return true;\r
1157 void NSViewComponentPeer::toFront (bool makeActiveWindow)\r
1159     if (isSharedWindow)\r
1160         [[view superview] addSubview: view\r
1161                           positioned: NSWindowAbove\r
1162                           relativeTo: nil];\r
1164     if (window != nil && component->isVisible())\r
1165     {\r
1166         if (makeActiveWindow)\r
1167             [window makeKeyAndOrderFront: nil];\r
1168         else\r
1169             [window orderFront: nil];\r
1171         if (! recursiveToFrontCall)\r
1172         {\r
1173             recursiveToFrontCall = true;\r
1174             Desktop::getInstance().getMainMouseSource().forceMouseCursorUpdate();\r
1175             handleBroughtToFront();\r
1176             recursiveToFrontCall = false;\r
1177         }\r
1178     }\r
1181 void NSViewComponentPeer::toBehind (ComponentPeer* other)\r
1183     NSViewComponentPeer* const otherPeer = dynamic_cast <NSViewComponentPeer*> (other);\r
1184     jassert (otherPeer != nullptr); // wrong type of window?\r
1186     if (otherPeer != nullptr)\r
1187     {\r
1188         if (isSharedWindow)\r
1189         {\r
1190             [[view superview] addSubview: view\r
1191                               positioned: NSWindowBelow\r
1192                               relativeTo: otherPeer->view];\r
1193         }\r
1194         else\r
1195         {\r
1196             [window orderWindow: NSWindowBelow\r
1197                      relativeTo: [otherPeer->window windowNumber]];\r
1198         }\r
1199     }\r
1202 void NSViewComponentPeer::setIcon (const Image& /*newIcon*/)\r
1204     // to do..\r
1207 //==============================================================================\r
1208 void NSViewComponentPeer::viewFocusGain()\r
1210     if (currentlyFocusedPeer != this)\r
1211     {\r
1212         if (ComponentPeer::isValidPeer (currentlyFocusedPeer))\r
1213             currentlyFocusedPeer->handleFocusLoss();\r
1215         currentlyFocusedPeer = this;\r
1216         handleFocusGain();\r
1217     }\r
1220 void NSViewComponentPeer::viewFocusLoss()\r
1222     if (currentlyFocusedPeer == this)\r
1223     {\r
1224         currentlyFocusedPeer = nullptr;\r
1225         handleFocusLoss();\r
1226     }\r
1229 void juce_HandleProcessFocusChange()\r
1231     NSViewComponentPeer::keysCurrentlyDown.clear();\r
1233     if (NSViewComponentPeer::isValidPeer (NSViewComponentPeer::currentlyFocusedPeer))\r
1234     {\r
1235         if (Process::isForegroundProcess())\r
1236         {\r
1237             NSViewComponentPeer::currentlyFocusedPeer->handleFocusGain();\r
1239             ModalComponentManager::getInstance()->bringModalComponentsToFront();\r
1240         }\r
1241         else\r
1242         {\r
1243             NSViewComponentPeer::currentlyFocusedPeer->handleFocusLoss();\r
1245             // turn kiosk mode off if we lose focus..\r
1246             Desktop::getInstance().setKioskModeComponent (nullptr);\r
1247         }\r
1248     }\r
1251 bool NSViewComponentPeer::isFocused() const\r
1253     return isSharedWindow ? this == currentlyFocusedPeer\r
1254                           : [window isKeyWindow];\r
1257 void NSViewComponentPeer::grabFocus()\r
1259     if (window != nil)\r
1260     {\r
1261         [window makeKeyWindow];\r
1262         [window makeFirstResponder: view];\r
1264         viewFocusGain();\r
1265     }\r
1268 void NSViewComponentPeer::textInputRequired (const Point<int>&)\r
1272 bool NSViewComponentPeer::handleKeyEvent (NSEvent* ev, bool isKeyDown)\r
1274     String unicode (nsStringToJuce ([ev characters]));\r
1275     String unmodified (nsStringToJuce ([ev charactersIgnoringModifiers]));\r
1276     int keyCode = getKeyCodeFromEvent (ev);\r
1278     //DBG ("unicode: " + unicode + " " + String::toHexString ((int) unicode[0]));\r
1279     //DBG ("unmodified: " + unmodified + " " + String::toHexString ((int) unmodified[0]));\r
1281     if (unicode.isNotEmpty() || keyCode != 0)\r
1282     {\r
1283         if (isKeyDown)\r
1284         {\r
1285             bool used = false;\r
1287             while (unicode.length() > 0)\r
1288             {\r
1289                 juce_wchar textCharacter = unicode[0];\r
1290                 unicode = unicode.substring (1);\r
1292                 if (([ev modifierFlags] & NSCommandKeyMask) != 0)\r
1293                     textCharacter = 0;\r
1295                 used = handleKeyUpOrDown (true) || used;\r
1296                 used = handleKeyPress (keyCode, textCharacter) || used;\r
1297             }\r
1299             return used;\r
1300         }\r
1301         else\r
1302         {\r
1303             if (handleKeyUpOrDown (false))\r
1304                 return true;\r
1305         }\r
1306     }\r
1308     return false;\r
1311 bool NSViewComponentPeer::redirectKeyDown (NSEvent* ev)\r
1313     updateKeysDown (ev, true);\r
1314     bool used = handleKeyEvent (ev, true);\r
1316     if (([ev modifierFlags] & NSCommandKeyMask) != 0)\r
1317     {\r
1318         // for command keys, the key-up event is thrown away, so simulate one..\r
1319         updateKeysDown (ev, false);\r
1320         used = (isValidPeer (this) && handleKeyEvent (ev, false)) || used;\r
1321     }\r
1323     // (If we're running modally, don't allow unused keystrokes to be passed\r
1324     // along to other blocked views..)\r
1325     if (Component::getCurrentlyModalComponent() != nullptr)\r
1326         used = true;\r
1328     return used;\r
1331 bool NSViewComponentPeer::redirectKeyUp (NSEvent* ev)\r
1333     updateKeysDown (ev, false);\r
1334     return handleKeyEvent (ev, false)\r
1335             || Component::getCurrentlyModalComponent() != nullptr;\r
1338 void NSViewComponentPeer::redirectModKeyChange (NSEvent* ev)\r
1340     keysCurrentlyDown.clear();\r
1341     handleKeyUpOrDown (true);\r
1343     updateModifiers (ev);\r
1344     handleModifierKeysChange();\r
1347 #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5\r
1348 bool NSViewComponentPeer::redirectPerformKeyEquivalent (NSEvent* ev)\r
1350     if ([ev type] == NSKeyDown)\r
1351         return redirectKeyDown (ev);\r
1352     else if ([ev type] == NSKeyUp)\r
1353         return redirectKeyUp (ev);\r
1355     return false;\r
1357 #endif\r
1359 //==============================================================================\r
1360 void NSViewComponentPeer::sendMouseEvent (NSEvent* ev)\r
1362     updateModifiers (ev);\r
1363     handleMouseEvent (0, getMousePos (ev, view), currentModifiers, getMouseTime (ev));\r
1366 void NSViewComponentPeer::redirectMouseDown (NSEvent* ev)\r
1368     currentModifiers = currentModifiers.withFlags (getModifierForButtonNumber ([ev buttonNumber]));\r
1369     sendMouseEvent (ev);\r
1372 void NSViewComponentPeer::redirectMouseUp (NSEvent* ev)\r
1374     currentModifiers = currentModifiers.withoutFlags (getModifierForButtonNumber ([ev buttonNumber]));\r
1375     sendMouseEvent (ev);\r
1376     showArrowCursorIfNeeded();\r
1379 void NSViewComponentPeer::redirectMouseDrag (NSEvent* ev)\r
1381     currentModifiers = currentModifiers.withFlags (getModifierForButtonNumber ([ev buttonNumber]));\r
1382     sendMouseEvent (ev);\r
1385 void NSViewComponentPeer::redirectMouseMove (NSEvent* ev)\r
1387     currentModifiers = currentModifiers.withoutMouseButtons();\r
1388     sendMouseEvent (ev);\r
1389     showArrowCursorIfNeeded();\r
1392 void NSViewComponentPeer::redirectMouseEnter (NSEvent* ev)\r
1394     Desktop::getInstance().getMainMouseSource().forceMouseCursorUpdate();\r
1395     currentModifiers = currentModifiers.withoutMouseButtons();\r
1396     sendMouseEvent (ev);\r
1399 void NSViewComponentPeer::redirectMouseExit (NSEvent* ev)\r
1401     currentModifiers = currentModifiers.withoutMouseButtons();\r
1402     sendMouseEvent (ev);\r
1405 void NSViewComponentPeer::redirectMouseWheel (NSEvent* ev)\r
1407     updateModifiers (ev);\r
1409     float x = 0, y = 0;\r
1411     @try\r
1412     {\r
1413         x = [ev deviceDeltaX] * 0.5f;\r
1414         y = [ev deviceDeltaY] * 0.5f;\r
1415     }\r
1416     @catch (...)\r
1417     {}\r
1419     if (x == 0 && y == 0)\r
1420     {\r
1421         x = [ev deltaX] * 10.0f;\r
1422         y = [ev deltaY] * 10.0f;\r
1423     }\r
1425     handleMouseWheel (0, getMousePos (ev, view), getMouseTime (ev), x, y);\r
1428 void NSViewComponentPeer::showArrowCursorIfNeeded()\r
1430     MouseInputSource& mouse = Desktop::getInstance().getMainMouseSource();\r
1432     if (mouse.getComponentUnderMouse() == nullptr\r
1433          && Desktop::getInstance().findComponentAt (mouse.getScreenPosition()) == nullptr)\r
1434     {\r
1435         [[NSCursor arrowCursor] set];\r
1436     }\r
1439 //==============================================================================\r
1440 BOOL NSViewComponentPeer::sendDragCallback (const int type, id <NSDraggingInfo> sender)\r
1442     NSString* bestType\r
1443         = [[sender draggingPasteboard] availableTypeFromArray: [view getSupportedDragTypes]];\r
1445     if (bestType == nil)\r
1446         return false;\r
1448     NSPoint p = [view convertPoint: [sender draggingLocation] fromView: nil];\r
1449     const Point<int> pos ((int) p.x, (int) ([view frame].size.height - p.y));\r
1451     NSPasteboard* pasteBoard = [sender draggingPasteboard];\r
1452     StringArray files;\r
1454     NSString* iTunesPasteboardType = @"CorePasteboardFlavorType 0x6974756E"; // 'itun'\r
1456     if (bestType == NSFilesPromisePboardType\r
1457          && [[pasteBoard types] containsObject: iTunesPasteboardType])\r
1458     {\r
1459         id list = [pasteBoard propertyListForType: iTunesPasteboardType];\r
1461         if ([list isKindOfClass: [NSDictionary class]])\r
1462         {\r
1463             NSDictionary* iTunesDictionary = (NSDictionary*) list;\r
1464             NSArray* tracks = [iTunesDictionary valueForKey: @"Tracks"];\r
1465             NSEnumerator* enumerator = [tracks objectEnumerator];\r
1466             NSDictionary* track;\r
1468             while ((track = [enumerator nextObject]) != nil)\r
1469             {\r
1470                 NSURL* url = [NSURL URLWithString: [track valueForKey: @"Location"]];\r
1472                 if ([url isFileURL])\r
1473                     files.add (nsStringToJuce ([url path]));\r
1474             }\r
1475         }\r
1476     }\r
1477     else\r
1478     {\r
1479         id list = [pasteBoard propertyListForType: NSFilenamesPboardType];\r
1481         if ([list isKindOfClass: [NSArray class]])\r
1482         {\r
1483             NSArray* items = (NSArray*) [pasteBoard propertyListForType: NSFilenamesPboardType];\r
1485             for (unsigned int i = 0; i < [items count]; ++i)\r
1486                 files.add (nsStringToJuce ((NSString*) [items objectAtIndex: i]));\r
1487         }\r
1488     }\r
1490     if (files.size() > 0)\r
1491     {\r
1492         switch (type)\r
1493         {\r
1494             case 0:   handleFileDragMove (files, pos); break;\r
1495             case 1:   handleFileDragExit (files); break;\r
1496             case 2:   handleFileDragDrop (files, pos); break;\r
1497             default:  jassertfalse; break;\r
1498         }\r
1500         return true;\r
1501     }\r
1503     return false;\r
1506 bool NSViewComponentPeer::isOpaque()\r
1508     return component == nullptr || component->isOpaque();\r
1511 void NSViewComponentPeer::drawRect (NSRect r)\r
1513     if (r.size.width < 1.0f || r.size.height < 1.0f)\r
1514         return;\r
1516     CGContextRef cg = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort];\r
1518     if (! component->isOpaque())\r
1519         CGContextClearRect (cg, CGContextGetClipBoundingBox (cg));\r
1521    #if USE_COREGRAPHICS_RENDERING\r
1522     if (usingCoreGraphics)\r
1523     {\r
1524         CoreGraphicsContext context (cg, (float) [view frame].size.height);\r
1526         insideDrawRect = true;\r
1527         handlePaint (context);\r
1528         insideDrawRect = false;\r
1529     }\r
1530     else\r
1531    #endif\r
1532     {\r
1533         Image temp (getComponent()->isOpaque() ? Image::RGB : Image::ARGB,\r
1534                     (int) (r.size.width + 0.5f),\r
1535                     (int) (r.size.height + 0.5f),\r
1536                     ! getComponent()->isOpaque());\r
1538         const int xOffset = -roundToInt (r.origin.x);\r
1539         const int yOffset = -roundToInt ([view frame].size.height - (r.origin.y + r.size.height));\r
1541         const NSRect* rects = nullptr;\r
1542         NSInteger numRects = 0;\r
1543         [view getRectsBeingDrawn: &rects count: &numRects];\r
1545         const Rectangle<int> clipBounds (temp.getBounds());\r
1547         RectangleList clip;\r
1548         for (int i = 0; i < numRects; ++i)\r
1549         {\r
1550             clip.addWithoutMerging (clipBounds.getIntersection (Rectangle<int> (roundToInt (rects[i].origin.x) + xOffset,\r
1551                                                                                 roundToInt ([view frame].size.height - (rects[i].origin.y + rects[i].size.height)) + yOffset,\r
1552                                                                                 roundToInt (rects[i].size.width),\r
1553                                                                                 roundToInt (rects[i].size.height))));\r
1554         }\r
1556         if (! clip.isEmpty())\r
1557         {\r
1558             LowLevelGraphicsSoftwareRenderer context (temp, xOffset, yOffset, clip);\r
1560             insideDrawRect = true;\r
1561             handlePaint (context);\r
1562             insideDrawRect = false;\r
1564             CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB();\r
1565             CGImageRef image = CoreGraphicsImage::createImage (temp, false, colourSpace, false);\r
1566             CGColorSpaceRelease (colourSpace);\r
1567             CGContextDrawImage (cg, CGRectMake (r.origin.x, r.origin.y, temp.getWidth(), temp.getHeight()), image);\r
1568             CGImageRelease (image);\r
1569         }\r
1570     }\r
1573 StringArray NSViewComponentPeer::getAvailableRenderingEngines()\r
1575     StringArray s (ComponentPeer::getAvailableRenderingEngines());\r
1577    #if USE_COREGRAPHICS_RENDERING\r
1578     s.add ("CoreGraphics Renderer");\r
1579    #endif\r
1581     return s;\r
1584 int NSViewComponentPeer::getCurrentRenderingEngine() const\r
1586     return usingCoreGraphics ? 1 : 0;\r
1589 void NSViewComponentPeer::setCurrentRenderingEngine (int index)\r
1591    #if USE_COREGRAPHICS_RENDERING\r
1592     if (usingCoreGraphics != (index > 0))\r
1593     {\r
1594         usingCoreGraphics = index > 0;\r
1595         [view setNeedsDisplay: true];\r
1596     }\r
1597    #endif\r
1600 bool NSViewComponentPeer::canBecomeKeyWindow()\r
1602     return (getStyleFlags() & JUCE_NAMESPACE::ComponentPeer::windowIgnoresKeyPresses) == 0;\r
1605 void NSViewComponentPeer::becomeKeyWindow()\r
1607     handleBroughtToFront();\r
1608     grabFocus();\r
1611 bool NSViewComponentPeer::windowShouldClose()\r
1613     if (! isValidPeer (this))\r
1614         return YES;\r
1616     handleUserClosingWindow();\r
1617     return NO;\r
1620 void NSViewComponentPeer::updateFullscreenStatus()\r
1622     if (hasNativeTitleBar())\r
1623     {\r
1624         const Rectangle<int> screen (getFrameSize().subtractedFrom (component->getParentMonitorArea()));\r
1625         const Rectangle<int> window (component->getScreenBounds());\r
1627         fullScreen = std::abs (screen.getX() - window.getX()) <= 2\r
1628                   && std::abs (screen.getY() - window.getY()) <= 2\r
1629                   && std::abs (screen.getRight() - window.getRight()) <= 2\r
1630                   && std::abs (screen.getBottom() - window.getBottom()) <= 2;\r
1631     }\r
1634 void NSViewComponentPeer::redirectMovedOrResized()\r
1636     updateFullscreenStatus();\r
1637     handleMovedOrResized();\r
1640 void NSViewComponentPeer::viewMovedToWindow()\r
1642     if (isSharedWindow)\r
1643         window = [view window];\r
1646 //==============================================================================\r
1647 void Desktop::createMouseInputSources()\r
1649     mouseSources.add (new MouseInputSource (0, true));\r
1652 //==============================================================================\r
1653 void Desktop::setKioskComponent (Component* kioskModeComponent, bool enableOrDisable, bool allowMenusAndBars)\r
1655    #if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6\r
1656     if (enableOrDisable)\r
1657     {\r
1658         [NSApp setPresentationOptions: (allowMenusAndBars ? (NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)\r
1659                                                           : (NSApplicationPresentationHideDock | NSApplicationPresentationHideMenuBar))];\r
1660         kioskModeComponent->setBounds (Desktop::getInstance().getMainMonitorArea (false));\r
1661     }\r
1662     else\r
1663     {\r
1664         [NSApp setPresentationOptions: NSApplicationPresentationDefault];\r
1665     }\r
1666    #else\r
1667     if (enableOrDisable)\r
1668     {\r
1669         SetSystemUIMode (kUIModeAllSuppressed, allowMenusAndBars ? kUIOptionAutoShowMenuBar : 0);\r
1670         kioskModeComponent->setBounds (Desktop::getInstance().getMainMonitorArea (false));\r
1671     }\r
1672     else\r
1673     {\r
1674         SetSystemUIMode (kUIModeNormal, 0);\r
1675     }\r
1676    #endif\r
1679 //==============================================================================\r
1680 void NSViewComponentPeer::repaint (const Rectangle<int>& area)\r
1682     if (insideDrawRect)\r
1683     {\r
1684         class AsyncRepaintMessage  : public CallbackMessage\r
1685         {\r
1686         public:\r
1687             AsyncRepaintMessage (NSViewComponentPeer* const peer_, const Rectangle<int>& rect_)\r
1688                 : peer (peer_), rect (rect_)\r
1689             {\r
1690             }\r
1692             void messageCallback()\r
1693             {\r
1694                 if (ComponentPeer::isValidPeer (peer))\r
1695                     peer->repaint (rect);\r
1696             }\r
1698         private:\r
1699             NSViewComponentPeer* const peer;\r
1700             const Rectangle<int> rect;\r
1701         };\r
1703         (new AsyncRepaintMessage (this, area))->post();\r
1704     }\r
1705     else\r
1706     {\r
1707         [view setNeedsDisplayInRect: NSMakeRect ((CGFloat) area.getX(), [view frame].size.height - (CGFloat) area.getBottom(),\r
1708                                                  (CGFloat) area.getWidth(), (CGFloat) area.getHeight())];\r
1709     }\r
1712 void NSViewComponentPeer::performAnyPendingRepaintsNow()\r
1714     [view displayIfNeeded];\r
1717 ComponentPeer* Component::createNewPeer (int styleFlags, void* windowToAttachTo)\r
1719     return new NSViewComponentPeer (this, styleFlags, (NSView*) windowToAttachTo);\r
1722 //==============================================================================\r
1723 Image juce_createIconForFile (const File& file)\r
1725     JUCE_AUTORELEASEPOOL\r
1727     NSImage* image = [[NSWorkspace sharedWorkspace] iconForFile: juceStringToNS (file.getFullPathName())];\r
1729     CoreGraphicsImage* result = new CoreGraphicsImage (Image::ARGB, (int) [image size].width, (int) [image size].height, true);\r
1731     [NSGraphicsContext saveGraphicsState];\r
1732     [NSGraphicsContext setCurrentContext: [NSGraphicsContext graphicsContextWithGraphicsPort: result->context flipped: false]];\r
1734     [image drawAtPoint: NSMakePoint (0, 0)\r
1735               fromRect: NSMakeRect (0, 0, [image size].width, [image size].height)\r
1736              operation: NSCompositeSourceOver fraction: 1.0f];\r
1738     [[NSGraphicsContext currentContext] flushGraphics];\r
1739     [NSGraphicsContext restoreGraphicsState];\r
1741     return Image (result);\r
1744 //==============================================================================\r
1745 const int KeyPress::spaceKey        = ' ';\r
1746 const int KeyPress::returnKey       = 0x0d;\r
1747 const int KeyPress::escapeKey       = 0x1b;\r
1748 const int KeyPress::backspaceKey    = 0x7f;\r
1749 const int KeyPress::leftKey         = NSLeftArrowFunctionKey;\r
1750 const int KeyPress::rightKey        = NSRightArrowFunctionKey;\r
1751 const int KeyPress::upKey           = NSUpArrowFunctionKey;\r
1752 const int KeyPress::downKey         = NSDownArrowFunctionKey;\r
1753 const int KeyPress::pageUpKey       = NSPageUpFunctionKey;\r
1754 const int KeyPress::pageDownKey     = NSPageDownFunctionKey;\r
1755 const int KeyPress::endKey          = NSEndFunctionKey;\r
1756 const int KeyPress::homeKey         = NSHomeFunctionKey;\r
1757 const int KeyPress::deleteKey       = NSDeleteFunctionKey;\r
1758 const int KeyPress::insertKey       = -1;\r
1759 const int KeyPress::tabKey          = 9;\r
1760 const int KeyPress::F1Key           = NSF1FunctionKey;\r
1761 const int KeyPress::F2Key           = NSF2FunctionKey;\r
1762 const int KeyPress::F3Key           = NSF3FunctionKey;\r
1763 const int KeyPress::F4Key           = NSF4FunctionKey;\r
1764 const int KeyPress::F5Key           = NSF5FunctionKey;\r
1765 const int KeyPress::F6Key           = NSF6FunctionKey;\r
1766 const int KeyPress::F7Key           = NSF7FunctionKey;\r
1767 const int KeyPress::F8Key           = NSF8FunctionKey;\r
1768 const int KeyPress::F9Key           = NSF9FunctionKey;\r
1769 const int KeyPress::F10Key          = NSF10FunctionKey;\r
1770 const int KeyPress::F11Key          = NSF1FunctionKey;\r
1771 const int KeyPress::F12Key          = NSF12FunctionKey;\r
1772 const int KeyPress::F13Key          = NSF13FunctionKey;\r
1773 const int KeyPress::F14Key          = NSF14FunctionKey;\r
1774 const int KeyPress::F15Key          = NSF15FunctionKey;\r
1775 const int KeyPress::F16Key          = NSF16FunctionKey;\r
1776 const int KeyPress::numberPad0      = 0x30020;\r
1777 const int KeyPress::numberPad1      = 0x30021;\r
1778 const int KeyPress::numberPad2      = 0x30022;\r
1779 const int KeyPress::numberPad3      = 0x30023;\r
1780 const int KeyPress::numberPad4      = 0x30024;\r
1781 const int KeyPress::numberPad5      = 0x30025;\r
1782 const int KeyPress::numberPad6      = 0x30026;\r
1783 const int KeyPress::numberPad7      = 0x30027;\r
1784 const int KeyPress::numberPad8      = 0x30028;\r
1785 const int KeyPress::numberPad9      = 0x30029;\r
1786 const int KeyPress::numberPadAdd            = 0x3002a;\r
1787 const int KeyPress::numberPadSubtract       = 0x3002b;\r
1788 const int KeyPress::numberPadMultiply       = 0x3002c;\r
1789 const int KeyPress::numberPadDivide         = 0x3002d;\r
1790 const int KeyPress::numberPadSeparator      = 0x3002e;\r
1791 const int KeyPress::numberPadDecimalPoint   = 0x3002f;\r
1792 const int KeyPress::numberPadEquals         = 0x30030;\r
1793 const int KeyPress::numberPadDelete         = 0x30031;\r
1794 const int KeyPress::playKey         = 0x30000;\r
1795 const int KeyPress::stopKey         = 0x30001;\r
1796 const int KeyPress::fastForwardKey  = 0x30002;\r
1797 const int KeyPress::rewindKey       = 0x30003;\r
1799 #endif\r