5d6dc988032bc973ca26fb0258f39c34da65be76
[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   myHView = [[myHWindow contentView] retain];
142
143   NSString* aTitleNs = [[NSString alloc] initWithUTF8String: theTitle];
144   [myHWindow setTitle: aTitleNs];
145   [aTitleNs release];
146
147   // do not destroy NSWindow on close - we didn't handle it!
148   [myHWindow setReleasedWhenClosed: NO];
149 #endif
150 }
151
152 // =======================================================================
153 // function : Cocoa_Window
154 // purpose  :
155 // =======================================================================
156 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
157 Cocoa_Window::Cocoa_Window (UIView* theViewNS)
158 : Aspect_Window(),
159 #else
160 Cocoa_Window::Cocoa_Window (NSView* theViewNS)
161 : Aspect_Window(),
162   myHWindow (NULL),
163 #endif
164   myHView   (NULL),
165   myXLeft   (0),
166   myYTop    (0),
167   myXRight  (512),
168   myYBottom (512)
169 {
170 #if defined(HAVE_OBJC_ARC)
171   myHView = theViewNS;
172 #else
173   myHView = [theViewNS retain];
174 #endif
175   DoResize();
176 }
177
178 // =======================================================================
179 // function : ~Cocoa_Window
180 // purpose  :
181 // =======================================================================
182 Cocoa_Window::~Cocoa_Window()
183 {
184 #if !defined(HAVE_OBJC_ARC)
185   Cocoa_LocalPool aLocalPool;
186 #endif
187 #if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE)
188   if (myHWindow != NULL)
189   {
190   #if !defined(HAVE_OBJC_ARC)
191     //[myHWindow close];
192     [myHWindow release];
193   #endif
194     myHWindow = NULL;
195   }
196 #endif
197   if (myHView != NULL)
198   {
199   #if !defined(HAVE_OBJC_ARC)
200     [myHView release];
201   #endif
202     myHView = NULL;
203   }
204 }
205
206 // =======================================================================
207 // function : SetHView
208 // purpose  :
209 // =======================================================================
210 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
211 void Cocoa_Window::SetHView (UIView* theView)
212 {
213 #else
214 void Cocoa_Window::SetHView (NSView* theView)
215 {
216   if (myHWindow != NULL)
217   {
218     [myHWindow setContentView: theView];
219   }
220 #endif
221
222 #if defined(HAVE_OBJC_ARC)
223   myHView = theView;
224 #else
225   if (myHView != NULL)
226   {
227     [myHView release];
228     myHView = NULL;
229   }
230   myHView = [theView retain];
231 #endif
232 }
233
234 // =======================================================================
235 // function : IsMapped
236 // purpose  :
237 // =======================================================================
238 Standard_Boolean Cocoa_Window::IsMapped() const
239 {
240   if (IsVirtual())
241   {
242     return Standard_True;
243   }
244
245 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
246   return myHView != NULL;
247 #else
248   return myHView != NULL
249    &&  [[myHView window] isVisible];
250 #endif
251 }
252
253 // =======================================================================
254 // function : Map
255 // purpose  :
256 // =======================================================================
257 void Cocoa_Window::Map() const
258 {
259   if (IsVirtual())
260   {
261     return;
262   }
263
264   if (myHView != NULL)
265   {
266   #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
267     //
268   #else
269     [[myHView window] orderFront: NULL];
270   #endif
271   }
272 }
273
274 // =======================================================================
275 // function : Unmap
276 // purpose  :
277 // =======================================================================
278 void Cocoa_Window::Unmap() const
279 {
280   if (myHView != NULL)
281   {
282   #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
283     //
284   #else
285     [[myHView window] orderOut: NULL];
286   #endif
287   }
288 }
289
290 // =======================================================================
291 // function : DoResize
292 // purpose  :
293 // =======================================================================
294 Aspect_TypeOfResize Cocoa_Window::DoResize() const
295 {
296   if (myHView == NULL)
297   {
298     return Aspect_TOR_UNKNOWN;
299   }
300
301 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
302   CGRect aBounds = [myHView bounds];
303 #else
304   NSRect aBounds = [myHView bounds];
305 #endif
306   Standard_Integer aMask = 0;
307   Aspect_TypeOfResize aMode = Aspect_TOR_UNKNOWN;
308
309   if (Abs ((Standard_Integer )aBounds.origin.x                         - myXLeft  ) > 2) aMask |= 1;
310   if (Abs ((Standard_Integer )(aBounds.origin.x + aBounds.size.width)  - myXRight ) > 2) aMask |= 2;
311   if (Abs ((Standard_Integer )aBounds.origin.y                         - myYTop   ) > 2) aMask |= 4;
312   if (Abs ((Standard_Integer )(aBounds.origin.y + aBounds.size.height) - myYBottom) > 2) aMask |= 8;
313   switch (aMask)
314   {
315     case 0:  aMode = Aspect_TOR_NO_BORDER;               break;
316     case 1:  aMode = Aspect_TOR_LEFT_BORDER;             break;
317     case 2:  aMode = Aspect_TOR_RIGHT_BORDER;            break;
318     case 4:  aMode = Aspect_TOR_TOP_BORDER;              break;
319     case 5:  aMode = Aspect_TOR_LEFT_AND_TOP_BORDER;     break;
320     case 6:  aMode = Aspect_TOR_TOP_AND_RIGHT_BORDER;    break;
321     case 8:  aMode = Aspect_TOR_BOTTOM_BORDER;           break;
322     case 9:  aMode = Aspect_TOR_BOTTOM_AND_LEFT_BORDER;  break;
323     case 10: aMode = Aspect_TOR_RIGHT_AND_BOTTOM_BORDER; break;
324     default: break;
325   }
326
327   *((Standard_Integer* )&myXLeft   ) = (Standard_Integer )aBounds.origin.x;
328   *((Standard_Integer* )&myXRight  ) = (Standard_Integer )(aBounds.origin.x + aBounds.size.width);
329   *((Standard_Integer* )&myYTop    ) = (Standard_Integer )aBounds.origin.y;
330   *((Standard_Integer* )&myYBottom ) = (Standard_Integer )(aBounds.origin.y + aBounds.size.height);
331   return aMode;
332 }
333
334 // =======================================================================
335 // function : DoMapping
336 // purpose  :
337 // =======================================================================
338 Standard_Boolean Cocoa_Window::DoMapping() const
339 {
340   return Standard_True;
341 }
342
343 // =======================================================================
344 // function : Ratio
345 // purpose  :
346 // =======================================================================
347 Standard_Real Cocoa_Window::Ratio() const
348 {
349   if (myHView == NULL)
350   {
351     return 1.0;
352   }
353
354 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
355   CGRect aBounds = [myHView bounds];
356 #else
357   NSRect aBounds = [myHView bounds];
358 #endif
359   return Standard_Real (aBounds.size.width / aBounds.size.height);
360 }
361
362 // =======================================================================
363 // function : Position
364 // purpose  :
365 // =======================================================================
366 void Cocoa_Window::Position (Standard_Integer& X1, Standard_Integer& Y1,
367                              Standard_Integer& X2, Standard_Integer& Y2) const
368 {
369 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
370   CGRect aBounds = [myHView bounds];
371   X1 = 0;
372   Y1 = 0;
373   X2 = (Standard_Integer )aBounds.size.width;
374   Y2 = (Standard_Integer )aBounds.size.height;
375 #else
376   NSWindow* aWindow = [myHView window];
377   NSRect aWindowRect = [aWindow frame];
378   X1 = (Standard_Integer) aWindowRect.origin.x;
379   Y1 = getScreenBottom() - (Standard_Integer) aWindowRect.origin.y - (Standard_Integer) aWindowRect.size.height;
380   X2 = X1 + (Standard_Integer) aWindowRect.size.width;
381   Y2 = Y1 + (Standard_Integer) aWindowRect.size.height;
382 #endif
383 }
384
385 // =======================================================================
386 // function : Size
387 // purpose  :
388 // =======================================================================
389 void Cocoa_Window::Size (Standard_Integer& theWidth,
390                          Standard_Integer& theHeight) const
391 {
392   if (myHView == NULL)
393   {
394     return;
395   }
396
397 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
398   CGRect aBounds = [myHView bounds];
399 #else
400   NSRect aBounds = [myHView bounds];
401 #endif
402   theWidth  = (Standard_Integer )aBounds.size.width;
403   theHeight = (Standard_Integer )aBounds.size.height;
404 }
405
406 // =======================================================================
407 // function : SetTitle
408 // purpose  :
409 // =======================================================================
410 void Cocoa_Window::SetTitle (const TCollection_AsciiString& theTitle)
411 {
412   if (myHView == NULL)
413   {
414     return;
415   }
416
417 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
418   (void )theTitle;
419 #else
420   NSWindow* aWindow  = [myHView window];
421   NSString* aTitleNS = [[NSString alloc] initWithUTF8String: theTitle.ToCString()];
422   [aWindow setTitle: aTitleNS];
423   [aTitleNS release];
424 #endif
425 }
426
427 // =======================================================================
428 // function : InvalidateContent
429 // purpose  :
430 // =======================================================================
431 void Cocoa_Window::InvalidateContent (const Handle(Aspect_DisplayConnection)& )
432 {
433   if (myHView == NULL)
434   {
435     return;
436   }
437
438   if ([NSThread isMainThread])
439   {
440   #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
441     [myHView setNeedsDisplay];
442   #else
443     [myHView setNeedsDisplay: YES];
444   #endif
445   }
446   else
447   {
448     [myHView performSelectorOnMainThread: @selector(invalidateContentOcct:)
449                               withObject: NULL
450                            waitUntilDone: NO];
451   }
452 }
453
454 // =======================================================================
455 // function : VirtualKeyFromNative
456 // purpose  :
457 // =======================================================================
458 Aspect_VKey Cocoa_Window::VirtualKeyFromNative (Standard_Integer theKey)
459 {
460   switch (theKey)
461   {
462     case 0x00: return Aspect_VKey_A;
463     case 0x01: return Aspect_VKey_S;
464     case 0x02: return Aspect_VKey_D;
465     case 0x03: return Aspect_VKey_F;
466     case 0x04: return Aspect_VKey_H;
467     case 0x05: return Aspect_VKey_G;
468     case 0x06: return Aspect_VKey_Z;
469     case 0x07: return Aspect_VKey_X;
470     case 0x08: return Aspect_VKey_C;
471     case 0x09: return Aspect_VKey_V;
472     case 0x0A: return Aspect_VKey_UNKNOWN;
473     case 0x0B: return Aspect_VKey_B;
474     case 0x0C: return Aspect_VKey_Q;
475     case 0x0D: return Aspect_VKey_W;
476     case 0x0E: return Aspect_VKey_E;
477     case 0x0F: return Aspect_VKey_R;
478     case 0x10: return Aspect_VKey_Y;
479     case 0x11: return Aspect_VKey_T;
480     case 0x12: return Aspect_VKey_1;
481     case 0x13: return Aspect_VKey_2;
482     case 0x14: return Aspect_VKey_3;
483     case 0x15: return Aspect_VKey_4;
484     case 0x16: return Aspect_VKey_6;
485     case 0x17: return Aspect_VKey_5;
486     case 0x18: return Aspect_VKey_Plus;
487     case 0x19: return Aspect_VKey_9;
488     case 0x1A: return Aspect_VKey_7;
489     case 0x1B: return Aspect_VKey_Minus;
490     case 0x1C: return Aspect_VKey_8;
491     case 0x1D: return Aspect_VKey_0;
492     case 0x1E: return Aspect_VKey_BracketRight;
493     case 0x1F: return Aspect_VKey_O;
494     case 0x20: return Aspect_VKey_U;
495     case 0x21: return Aspect_VKey_BracketLeft;
496     case 0x22: return Aspect_VKey_I;
497     case 0x23: return Aspect_VKey_P;
498     case 0x24: return Aspect_VKey_Enter;
499     case 0x25: return Aspect_VKey_L;
500     case 0x26: return Aspect_VKey_J;
501     case 0x27: return Aspect_VKey_Apostrophe;
502     case 0x28: return Aspect_VKey_K;
503     case 0x29: return Aspect_VKey_Semicolon;
504     case 0x2A: return Aspect_VKey_Backslash;
505     case 0x2B: return Aspect_VKey_Comma; // 43, ',<'
506     case 0x2C: return Aspect_VKey_Slash; //ST_VK_OEM_2, // 44, '?/'
507     case 0x2D: return Aspect_VKey_N;
508     case 0x2E: return Aspect_VKey_M;
509     case 0x2F: return Aspect_VKey_Period; // 47, '.>'
510     case 0x30: return Aspect_VKey_Tab;
511     case 0x31: return Aspect_VKey_Space;
512     case 0x32: return Aspect_VKey_Tilde;  // '~`'
513     case 0x33: return Aspect_VKey_Backspace;
514     case 0x34: return Aspect_VKey_UNKNOWN;
515     case 0x35: return Aspect_VKey_Escape;
516     case 0x36: return Aspect_VKey_UNKNOWN; // Aspect_VKey_Cmd, right Command
517     case 0x37: return Aspect_VKey_UNKNOWN; // Aspect_VKey_Cmd, left  Command
518     case 0x38: return Aspect_VKey_Shift;   // left shift
519     case 0x39: return Aspect_VKey_UNKNOWN;
520     case 0x3A: return Aspect_VKey_Alt;     // left alt/option
521     case 0x3B: return Aspect_VKey_Control;
522     case 0x3C: return Aspect_VKey_Shift;   // right shift
523     case 0x3D: return Aspect_VKey_Alt;     // right alt/option
524     case 0x3E: return Aspect_VKey_UNKNOWN;
525     case 0x3F: return Aspect_VKey_UNKNOWN; // Aspect_VKey_Func, fn
526     case 0x40:
527     case 0x41:
528     case 0x42:
529     case 0x43:
530     case 0x44:
531     case 0x45:
532     case 0x46:
533     case 0x47:
534     case 0x48:
535     case 0x49:
536     case 0x4A:
537     case 0x4B: return Aspect_VKey_UNKNOWN;
538     case 0x4C: return Aspect_VKey_Enter;   // fn + return
539     case 0x4D:
540     case 0x4E:
541     case 0x4F:
542     case 0x50:
543     case 0x51:
544     case 0x52:
545     case 0x53:
546     case 0x54:
547     case 0x55:
548     case 0x56:
549     case 0x57:
550     case 0x58:
551     case 0x59:
552     case 0x5A:
553     case 0x5B:
554     case 0x5C:
555     case 0x5D:
556     case 0x5E:
557     case 0x5F: return Aspect_VKey_UNKNOWN;
558     case 0x60: return Aspect_VKey_F5;
559     case 0x61: return Aspect_VKey_F6;
560     case 0x62: return Aspect_VKey_F7;
561     case 0x63: return Aspect_VKey_F3;
562     case 0x64: return Aspect_VKey_F8;
563     case 0x65: return Aspect_VKey_F9;
564     //case 0x66: return Aspect_VKey_UNKNOWN;
565     case 0x67: return Aspect_VKey_F11;
566     //case 0x68: return Aspect_VKey_UNKNOWN;
567     //case 0x69: return Aspect_VKey_UNKNOWN;
568     //case 0x6A: return Aspect_VKey_UNKNOWN;
569     //case 0x6B: return Aspect_VKey_UNKNOWN;
570     //case 0x6C: return Aspect_VKey_UNKNOWN;
571     case 0x6D: return Aspect_VKey_F10;
572     //case 0x6E: return Aspect_VKey_UNKNOWN;
573     case 0x6F: return Aspect_VKey_F12;
574     //case 0x70: return Aspect_VKey_UNKNOWN;
575     //case 0x71: return Aspect_VKey_UNKNOWN;
576     //case 0x72: return Aspect_VKey_UNKNOWN;
577     case 0x73: return Aspect_VKey_Home;
578     case 0x74: return Aspect_VKey_PageUp;
579     case 0x75: return Aspect_VKey_Delete;
580     case 0x76: return Aspect_VKey_F4;
581     case 0x77: return Aspect_VKey_End;
582     case 0x78: return Aspect_VKey_F2;
583     case 0x79: return Aspect_VKey_PageDown;
584     case 0x7A: return Aspect_VKey_F1;
585     case 0x7B: return Aspect_VKey_Left;
586     case 0x7C: return Aspect_VKey_Right;
587     case 0x7D: return Aspect_VKey_Down;
588     case 0x7E: return Aspect_VKey_Up;
589     case 0x7F: return Aspect_VKey_UNKNOWN;
590   }
591   return Aspect_VKey_UNKNOWN;
592 }