63daaa8af4155b41364b1f255364f38bd51307d1
[occt.git] / src / Cocoa / Cocoa_Window.mm
1 // Created on: 2012-11-12
2 // Created by: Kirill GAVRILOV
3 // Copyright (c) 2012-2014 OPEN CASCADE SAS
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15
16 #import <TargetConditionals.h>
17
18 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
19   #import <UIKit/UIKit.h>
20 #else
21   #import <Cocoa/Cocoa.h>
22 #endif
23
24 #include <Cocoa_Window.hxx>
25
26 #include <Cocoa_LocalPool.hxx>
27
28 #include <Image_AlienPixMap.hxx>
29 #include <Aspect_Convert.hxx>
30 #include <Aspect_WindowDefinitionError.hxx>
31
32 IMPLEMENT_STANDARD_RTTIEXT(Cocoa_Window,Aspect_Window)
33
34 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
35   //
36 #else
37
38 #if !defined(MAC_OS_X_VERSION_10_12) || (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12)
39   // replacements for macOS versions before 10.12
40   #define NSWindowStyleMaskResizable NSResizableWindowMask
41   #define NSWindowStyleMaskClosable  NSClosableWindowMask
42   #define NSWindowStyleMaskTitled    NSTitledWindowMask
43 #endif
44
45 static Standard_Integer getScreenBottom()
46 {
47   Cocoa_LocalPool aLocalPool;
48   NSArray* aScreens = [NSScreen screens];
49   if (aScreens == NULL || [aScreens count] == 0)
50   {
51     return 0;
52   }
53
54   NSScreen* aScreen = (NSScreen* )[aScreens objectAtIndex: 0];
55   NSDictionary* aDict = [aScreen deviceDescription];
56   NSNumber* aNumber = [aDict objectForKey: @"NSScreenNumber"];
57   if (aNumber == NULL
58   || [aNumber isKindOfClass: [NSNumber class]] == NO)
59   {
60     return 0;
61   }
62
63   CGDirectDisplayID aDispId = [aNumber unsignedIntValue];
64   CGRect aRect = CGDisplayBounds(aDispId);
65   return Standard_Integer(aRect.origin.y + aRect.size.height);
66 }
67 #endif
68
69 //! Extension for Cocoa_Window::InvalidateContent().
70 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
71   @interface UIView (UIViewOcctAdditions)
72   - (void )invalidateContentOcct: (id )theSender;
73   @end
74   @implementation UIView (UIViewOcctAdditions)
75   - (void )invalidateContentOcct: (id )theSender
76   {
77     (void )theSender;
78     [self setNeedsDisplay];
79   }
80   @end
81 #else
82   @interface NSView (NSViewOcctAdditions)
83   - (void )invalidateContentOcct: (id )theSender;
84   @end
85   @implementation NSView (NSViewOcctAdditions)
86   - (void )invalidateContentOcct: (id )theSender
87   {
88     (void )theSender;
89     [self setNeedsDisplay: YES];
90   }
91   @end
92 #endif
93
94 // =======================================================================
95 // function : Cocoa_Window
96 // purpose  :
97 // =======================================================================
98 Cocoa_Window::Cocoa_Window (const Standard_CString theTitle,
99                             const Standard_Integer thePxLeft,
100                             const Standard_Integer thePxTop,
101                             const Standard_Integer thePxWidth,
102                             const Standard_Integer thePxHeight)
103 : Aspect_Window (),
104 #if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE)
105   myHWindow (NULL),
106 #endif
107   myHView   (NULL),
108   myXLeft   (thePxLeft),
109   myYTop    (thePxTop),
110   myXRight  (thePxLeft + thePxWidth),
111   myYBottom (thePxTop + thePxHeight)
112 {
113 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
114   //
115 #else
116   if (thePxWidth <= 0 || thePxHeight <= 0)
117   {
118     throw Aspect_WindowDefinitionError("Coordinate(s) out of range");
119   }
120   else if (NSApp == NULL)
121   {
122     throw Aspect_WindowDefinitionError("Cocoa application should be instantiated before window");
123     return;
124   }
125
126   // convert top-bottom coordinates to bottom-top (Cocoa)
127   myYTop    = getScreenBottom() - myYBottom;
128   myYBottom = myYTop + thePxHeight;
129
130   Cocoa_LocalPool aLocalPool;
131   NSUInteger aWinStyle = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable;
132   NSRect aRectNs = NSMakeRect (float(myXLeft), float(myYTop), float(thePxWidth), float(thePxHeight));
133   myHWindow = [[NSWindow alloc] initWithContentRect: aRectNs
134                                           styleMask: aWinStyle
135                                             backing: NSBackingStoreBuffered
136                                               defer: NO];
137   if (myHWindow == NULL)
138   {
139     throw Aspect_WindowDefinitionError("Unable to create window");
140   }
141   // for the moment, OpenGL renderer is expected to output sRGB colorspace
142   [myHWindow setColorSpace: [NSColorSpace sRGBColorSpace]];
143   myHView = [[myHWindow contentView] retain];
144
145   NSString* aTitleNs = [[NSString alloc] initWithUTF8String: theTitle];
146   [myHWindow setTitle: aTitleNs];
147   [aTitleNs release];
148
149   // do not destroy NSWindow on close - we didn't handle it!
150   [myHWindow setReleasedWhenClosed: NO];
151 #endif
152 }
153
154 // =======================================================================
155 // function : Cocoa_Window
156 // purpose  :
157 // =======================================================================
158 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
159 Cocoa_Window::Cocoa_Window (UIView* theViewNS)
160 : Aspect_Window(),
161 #else
162 Cocoa_Window::Cocoa_Window (NSView* theViewNS)
163 : Aspect_Window(),
164   myHWindow (NULL),
165 #endif
166   myHView   (NULL),
167   myXLeft   (0),
168   myYTop    (0),
169   myXRight  (512),
170   myYBottom (512)
171 {
172 #if defined(HAVE_OBJC_ARC)
173   myHView = theViewNS;
174 #else
175   myHView = [theViewNS retain];
176 #endif
177   DoResize();
178 }
179
180 // =======================================================================
181 // function : ~Cocoa_Window
182 // purpose  :
183 // =======================================================================
184 Cocoa_Window::~Cocoa_Window()
185 {
186 #if !defined(HAVE_OBJC_ARC)
187   Cocoa_LocalPool aLocalPool;
188 #endif
189 #if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE)
190   if (myHWindow != NULL)
191   {
192   #if !defined(HAVE_OBJC_ARC)
193     //[myHWindow close];
194     [myHWindow release];
195   #endif
196     myHWindow = NULL;
197   }
198 #endif
199   if (myHView != NULL)
200   {
201   #if !defined(HAVE_OBJC_ARC)
202     [myHView release];
203   #endif
204     myHView = NULL;
205   }
206 }
207
208 // =======================================================================
209 // function : SetHView
210 // purpose  :
211 // =======================================================================
212 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
213 void Cocoa_Window::SetHView (UIView* theView)
214 {
215 #else
216 void Cocoa_Window::SetHView (NSView* theView)
217 {
218   if (myHWindow != NULL)
219   {
220     [myHWindow setContentView: theView];
221   }
222 #endif
223
224 #if defined(HAVE_OBJC_ARC)
225   myHView = theView;
226 #else
227   if (myHView != NULL)
228   {
229     [myHView release];
230     myHView = NULL;
231   }
232   myHView = [theView retain];
233 #endif
234 }
235
236 // =======================================================================
237 // function : IsMapped
238 // purpose  :
239 // =======================================================================
240 Standard_Boolean Cocoa_Window::IsMapped() const
241 {
242   if (IsVirtual())
243   {
244     return Standard_True;
245   }
246
247 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
248   return myHView != NULL;
249 #else
250   return myHView != NULL
251    &&  [[myHView window] isVisible];
252 #endif
253 }
254
255 // =======================================================================
256 // function : Map
257 // purpose  :
258 // =======================================================================
259 void Cocoa_Window::Map() const
260 {
261   if (IsVirtual())
262   {
263     return;
264   }
265
266   if (myHView != NULL)
267   {
268   #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
269     //
270   #else
271     [[myHView window] orderFront: NULL];
272   #endif
273   }
274 }
275
276 // =======================================================================
277 // function : Unmap
278 // purpose  :
279 // =======================================================================
280 void Cocoa_Window::Unmap() const
281 {
282   if (myHView != NULL)
283   {
284   #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
285     //
286   #else
287     [[myHView window] orderOut: NULL];
288   #endif
289   }
290 }
291
292 // =======================================================================
293 // function : DoResize
294 // purpose  :
295 // =======================================================================
296 Aspect_TypeOfResize Cocoa_Window::DoResize() const
297 {
298   if (myHView == NULL)
299   {
300     return Aspect_TOR_UNKNOWN;
301   }
302
303 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
304   CGRect aBounds = [myHView bounds];
305 #else
306   NSRect aBounds = [myHView bounds];
307 #endif
308   Standard_Integer aMask = 0;
309   Aspect_TypeOfResize aMode = Aspect_TOR_UNKNOWN;
310
311   if (Abs ((Standard_Integer )aBounds.origin.x                         - myXLeft  ) > 2) aMask |= 1;
312   if (Abs ((Standard_Integer )(aBounds.origin.x + aBounds.size.width)  - myXRight ) > 2) aMask |= 2;
313   if (Abs ((Standard_Integer )aBounds.origin.y                         - myYTop   ) > 2) aMask |= 4;
314   if (Abs ((Standard_Integer )(aBounds.origin.y + aBounds.size.height) - myYBottom) > 2) aMask |= 8;
315   switch (aMask)
316   {
317     case 0:  aMode = Aspect_TOR_NO_BORDER;               break;
318     case 1:  aMode = Aspect_TOR_LEFT_BORDER;             break;
319     case 2:  aMode = Aspect_TOR_RIGHT_BORDER;            break;
320     case 4:  aMode = Aspect_TOR_TOP_BORDER;              break;
321     case 5:  aMode = Aspect_TOR_LEFT_AND_TOP_BORDER;     break;
322     case 6:  aMode = Aspect_TOR_TOP_AND_RIGHT_BORDER;    break;
323     case 8:  aMode = Aspect_TOR_BOTTOM_BORDER;           break;
324     case 9:  aMode = Aspect_TOR_BOTTOM_AND_LEFT_BORDER;  break;
325     case 10: aMode = Aspect_TOR_RIGHT_AND_BOTTOM_BORDER; break;
326     default: break;
327   }
328
329   *((Standard_Integer* )&myXLeft   ) = (Standard_Integer )aBounds.origin.x;
330   *((Standard_Integer* )&myXRight  ) = (Standard_Integer )(aBounds.origin.x + aBounds.size.width);
331   *((Standard_Integer* )&myYTop    ) = (Standard_Integer )aBounds.origin.y;
332   *((Standard_Integer* )&myYBottom ) = (Standard_Integer )(aBounds.origin.y + aBounds.size.height);
333   return aMode;
334 }
335
336 // =======================================================================
337 // function : DoMapping
338 // purpose  :
339 // =======================================================================
340 Standard_Boolean Cocoa_Window::DoMapping() const
341 {
342   return Standard_True;
343 }
344
345 // =======================================================================
346 // function : Ratio
347 // purpose  :
348 // =======================================================================
349 Standard_Real Cocoa_Window::Ratio() const
350 {
351   if (myHView == NULL)
352   {
353     return 1.0;
354   }
355
356 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
357   CGRect aBounds = [myHView bounds];
358 #else
359   NSRect aBounds = [myHView bounds];
360 #endif
361   return Standard_Real (aBounds.size.width / aBounds.size.height);
362 }
363
364 // =======================================================================
365 // function : Position
366 // purpose  :
367 // =======================================================================
368 void Cocoa_Window::Position (Standard_Integer& X1, Standard_Integer& Y1,
369                              Standard_Integer& X2, Standard_Integer& Y2) const
370 {
371 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
372   CGRect aBounds = [myHView bounds];
373   X1 = 0;
374   Y1 = 0;
375   X2 = (Standard_Integer )aBounds.size.width;
376   Y2 = (Standard_Integer )aBounds.size.height;
377 #else
378   NSWindow* aWindow = [myHView window];
379   NSRect aWindowRect = [aWindow frame];
380   X1 = (Standard_Integer) aWindowRect.origin.x;
381   Y1 = getScreenBottom() - (Standard_Integer) aWindowRect.origin.y - (Standard_Integer) aWindowRect.size.height;
382   X2 = X1 + (Standard_Integer) aWindowRect.size.width;
383   Y2 = Y1 + (Standard_Integer) aWindowRect.size.height;
384 #endif
385 }
386
387 // =======================================================================
388 // function : Size
389 // purpose  :
390 // =======================================================================
391 void Cocoa_Window::Size (Standard_Integer& theWidth,
392                          Standard_Integer& theHeight) const
393 {
394   if (myHView == NULL)
395   {
396     return;
397   }
398
399 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
400   CGRect aBounds = [myHView bounds];
401 #else
402   NSRect aBounds = [myHView bounds];
403 #endif
404   theWidth  = (Standard_Integer )aBounds.size.width;
405   theHeight = (Standard_Integer )aBounds.size.height;
406 }
407
408 // =======================================================================
409 // function : SetTitle
410 // purpose  :
411 // =======================================================================
412 void Cocoa_Window::SetTitle (const TCollection_AsciiString& theTitle)
413 {
414   if (myHView == NULL)
415   {
416     return;
417   }
418
419 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
420   (void )theTitle;
421 #else
422   NSWindow* aWindow  = [myHView window];
423   NSString* aTitleNS = [[NSString alloc] initWithUTF8String: theTitle.ToCString()];
424   [aWindow setTitle: aTitleNS];
425   [aTitleNS release];
426 #endif
427 }
428
429 // =======================================================================
430 // function : InvalidateContent
431 // purpose  :
432 // =======================================================================
433 void Cocoa_Window::InvalidateContent (const Handle(Aspect_DisplayConnection)& )
434 {
435   if (myHView == NULL)
436   {
437     return;
438   }
439
440   if ([NSThread isMainThread])
441   {
442   #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
443     [myHView setNeedsDisplay];
444   #else
445     [myHView setNeedsDisplay: YES];
446   #endif
447   }
448   else
449   {
450     [myHView performSelectorOnMainThread: @selector(invalidateContentOcct:)
451                               withObject: NULL
452                            waitUntilDone: NO];
453   }
454 }
455
456 // =======================================================================
457 // function : VirtualKeyFromNative
458 // purpose  :
459 // =======================================================================
460 Aspect_VKey Cocoa_Window::VirtualKeyFromNative (Standard_Integer theKey)
461 {
462   switch (theKey)
463   {
464     case 0x00: return Aspect_VKey_A;
465     case 0x01: return Aspect_VKey_S;
466     case 0x02: return Aspect_VKey_D;
467     case 0x03: return Aspect_VKey_F;
468     case 0x04: return Aspect_VKey_H;
469     case 0x05: return Aspect_VKey_G;
470     case 0x06: return Aspect_VKey_Z;
471     case 0x07: return Aspect_VKey_X;
472     case 0x08: return Aspect_VKey_C;
473     case 0x09: return Aspect_VKey_V;
474     case 0x0A: return Aspect_VKey_UNKNOWN;
475     case 0x0B: return Aspect_VKey_B;
476     case 0x0C: return Aspect_VKey_Q;
477     case 0x0D: return Aspect_VKey_W;
478     case 0x0E: return Aspect_VKey_E;
479     case 0x0F: return Aspect_VKey_R;
480     case 0x10: return Aspect_VKey_Y;
481     case 0x11: return Aspect_VKey_T;
482     case 0x12: return Aspect_VKey_1;
483     case 0x13: return Aspect_VKey_2;
484     case 0x14: return Aspect_VKey_3;
485     case 0x15: return Aspect_VKey_4;
486     case 0x16: return Aspect_VKey_6;
487     case 0x17: return Aspect_VKey_5;
488     case 0x18: return Aspect_VKey_Plus;
489     case 0x19: return Aspect_VKey_9;
490     case 0x1A: return Aspect_VKey_7;
491     case 0x1B: return Aspect_VKey_Minus;
492     case 0x1C: return Aspect_VKey_8;
493     case 0x1D: return Aspect_VKey_0;
494     case 0x1E: return Aspect_VKey_BracketRight;
495     case 0x1F: return Aspect_VKey_O;
496     case 0x20: return Aspect_VKey_U;
497     case 0x21: return Aspect_VKey_BracketLeft;
498     case 0x22: return Aspect_VKey_I;
499     case 0x23: return Aspect_VKey_P;
500     case 0x24: return Aspect_VKey_Enter;
501     case 0x25: return Aspect_VKey_L;
502     case 0x26: return Aspect_VKey_J;
503     case 0x27: return Aspect_VKey_Apostrophe;
504     case 0x28: return Aspect_VKey_K;
505     case 0x29: return Aspect_VKey_Semicolon;
506     case 0x2A: return Aspect_VKey_Backslash;
507     case 0x2B: return Aspect_VKey_Comma; // 43, ',<'
508     case 0x2C: return Aspect_VKey_Slash; //ST_VK_OEM_2, // 44, '?/'
509     case 0x2D: return Aspect_VKey_N;
510     case 0x2E: return Aspect_VKey_M;
511     case 0x2F: return Aspect_VKey_Period; // 47, '.>'
512     case 0x30: return Aspect_VKey_Tab;
513     case 0x31: return Aspect_VKey_Space;
514     case 0x32: return Aspect_VKey_Tilde;  // '~`'
515     case 0x33: return Aspect_VKey_Backspace;
516     case 0x34: return Aspect_VKey_UNKNOWN;
517     case 0x35: return Aspect_VKey_Escape;
518     case 0x36: return Aspect_VKey_UNKNOWN; // Aspect_VKey_Cmd, right Command
519     case 0x37: return Aspect_VKey_UNKNOWN; // Aspect_VKey_Cmd, left  Command
520     case 0x38: return Aspect_VKey_Shift;   // left shift
521     case 0x39: return Aspect_VKey_UNKNOWN;
522     case 0x3A: return Aspect_VKey_Alt;     // left alt/option
523     case 0x3B: return Aspect_VKey_Control;
524     case 0x3C: return Aspect_VKey_Shift;   // right shift
525     case 0x3D: return Aspect_VKey_Alt;     // right alt/option
526     case 0x3E: return Aspect_VKey_UNKNOWN;
527     case 0x3F: return Aspect_VKey_UNKNOWN; // Aspect_VKey_Func, fn
528     case 0x40:
529     case 0x41:
530     case 0x42:
531     case 0x43:
532     case 0x44:
533     case 0x45:
534     case 0x46:
535     case 0x47:
536     case 0x48:
537     case 0x49:
538     case 0x4A:
539     case 0x4B: return Aspect_VKey_UNKNOWN;
540     case 0x4C: return Aspect_VKey_Enter;   // fn + return
541     case 0x4D:
542     case 0x4E:
543     case 0x4F:
544     case 0x50:
545     case 0x51:
546     case 0x52:
547     case 0x53:
548     case 0x54:
549     case 0x55:
550     case 0x56:
551     case 0x57:
552     case 0x58:
553     case 0x59:
554     case 0x5A:
555     case 0x5B:
556     case 0x5C:
557     case 0x5D:
558     case 0x5E:
559     case 0x5F: return Aspect_VKey_UNKNOWN;
560     case 0x60: return Aspect_VKey_F5;
561     case 0x61: return Aspect_VKey_F6;
562     case 0x62: return Aspect_VKey_F7;
563     case 0x63: return Aspect_VKey_F3;
564     case 0x64: return Aspect_VKey_F8;
565     case 0x65: return Aspect_VKey_F9;
566     //case 0x66: return Aspect_VKey_UNKNOWN;
567     case 0x67: return Aspect_VKey_F11;
568     //case 0x68: return Aspect_VKey_UNKNOWN;
569     //case 0x69: return Aspect_VKey_UNKNOWN;
570     //case 0x6A: return Aspect_VKey_UNKNOWN;
571     //case 0x6B: return Aspect_VKey_UNKNOWN;
572     //case 0x6C: return Aspect_VKey_UNKNOWN;
573     case 0x6D: return Aspect_VKey_F10;
574     //case 0x6E: return Aspect_VKey_UNKNOWN;
575     case 0x6F: return Aspect_VKey_F12;
576     //case 0x70: return Aspect_VKey_UNKNOWN;
577     //case 0x71: return Aspect_VKey_UNKNOWN;
578     //case 0x72: return Aspect_VKey_UNKNOWN;
579     case 0x73: return Aspect_VKey_Home;
580     case 0x74: return Aspect_VKey_PageUp;
581     case 0x75: return Aspect_VKey_Delete;
582     case 0x76: return Aspect_VKey_F4;
583     case 0x77: return Aspect_VKey_End;
584     case 0x78: return Aspect_VKey_F2;
585     case 0x79: return Aspect_VKey_PageDown;
586     case 0x7A: return Aspect_VKey_F1;
587     case 0x7B: return Aspect_VKey_Left;
588     case 0x7C: return Aspect_VKey_Right;
589     case 0x7D: return Aspect_VKey_Down;
590     case 0x7E: return Aspect_VKey_Up;
591     case 0x7F: return Aspect_VKey_UNKNOWN;
592   }
593   return Aspect_VKey_UNKNOWN;
594 }